.net 是否应该允许域对象暂时无效,以及该决定如何影响验证技术?
我正在基于MVVM(使用Winforms绑定)编写一个VB.NET Winforms项目。我的直觉是永远不允许域实体处于无效状态。这要求我在构造函数中对新实体进行验证检查,并在每个setter中对现有实体进行验证检查:.net 是否应该允许域对象暂时无效,以及该决定如何影响验证技术?,.net,validation,domain-driven-design,.net,Validation,Domain Driven Design,我正在基于MVVM(使用Winforms绑定)编写一个VB.NET Winforms项目。我的直觉是永远不允许域实体处于无效状态。这要求我在构造函数中对新实体进行验证检查,并在每个setter中对现有实体进行验证检查: Public Class Product Public Sub New(ProductID as Integer, Name as String) If ProductID > 0 AndAlso Name.Length > 5 Then
Public Class Product
Public Sub New(ProductID as Integer, Name as String)
If ProductID > 0 AndAlso Name.Length > 5 Then
_ProductID = ProductID
_Name = Name
Else
Throw New InvalidProductException
End If
End Sub
Private _ProductID as Integer
Public Property ProductID as Integer
Set(value as String)
If value > 0 then
_ProductID = value
Else
Throw New InvalidProductException
End If
End Set
End Property
'Same principle as above for Name setter.
End Class
然后,我遇到了数据注释,看起来很流畅。我注意到,大多数使用数据注释的人都允许域实体暂时失效,然后在稍后的某个时候调用validate.ValidateObject来验证实体。此时,该实体无效,原始状态已丢失,除非您有其他机制将其回滚
两个问题:
1) 是否允许域实体暂时无效
2) 根据您对#1的回答,您使用什么技术来验证实体?不,在我看来,域实体永远不应该被允许无效,即使是暂时的。问题是,如果您允许域无效,就像您在问题中描述的那样,随着复杂性的增加,引入新规则变得越来越困难。例如,您允许实体由于某些属性而无效,假设稍后将对其进行验证。但在此之前,有人添加了另一条规则,该规则根据同一属性改变其结果-您如何知道该规则的行为是否正确?你没有。相信我,它经常发生在非平凡领域 不允许状态无效的另一个原因是,在某些情况下,它可能会给ORM带来问题-我个人看到过一个问题,涉及NHibernate缓存和子实体,这些子实体无效,但不知何故仍保留在缓存中,但我记不起任何具体细节 我倾向于使用基于验证规则和验证结果的技术。简而言之,实体上的大多数方法都是通过以下方式实现的(如果您不介意的话,可以使用C#):
公共虚拟无效更改claimeventdate(DateTimeOffset newDate)
{
var operationResult=ValidatorOf
.Validate()
.WithCriticalRuleOf().WithParam(新日期)
.WithCriticalRuleOf().WithParam(新日期)
.WithCriticalRuleOf().WithParam(新日期)
.WithCriticalRuleOf().WithParam(新日期)
.用于操作(本);
if(操作结果。操作失败)
{
抛出新的无效业务操作(operationResult);
}
SomeDate=newDate;
}
这段代码最重要的一点是,即使在更改实体之前,也会检查某些验证规则。这个例子展示了结果集的使用,因为即使验证成功,我也经常需要提供有关验证的信息(换句话说,我有失败的验证,必须向用户显示有关验证的信息;但是域实体仍然有效)
OperationResultSet
和ValidatorOf
是非常简单的基础结构类,允许使用fluent接口轻松添加新的验证器。验证器实现为实现ivalidater
接口的类,允许实现非常复杂的验证规则,并且更易于测试他们也各自独立
我的观点是,验证应该在对域实体进行更改之前执行——使用正确的约定和一些基础设施,它甚至可以简化代码结构
编辑说明:由于对这个答案有一些批评的声音,我决定将示例代码更改为引发异常而不是返回结果的代码。尽管我仍然认为这是我的场景的一种方式,但我同意,如果不指定完整的上下文,这可能会产生误导-异常应该是第一个选项和其他因素应该存在以选择备选方案。你的直觉是正确的。在DDD中,永远不应该允许对象进入从域角度看无效的状态。即使是暂时的。对象应该保护其内部不变量,这是非常基本的OOP。否则它将不是一个对象,而只是一个哑数据通常,人们会被UI框架弄糊涂,或者过度概括“验证”一词 例如,您系统中的每个产品都应该有SKU。或者每个客户都应该有社会保险号。强制执行这些规则是产品和客户实体的直接责任。好的异常会让客户代码意识到它打破了一些不变。强制执行此规则不是UI的责任,也不是bstract“Validator”。如果允许在实体外部强制执行此规则,您将:
- 最终以无效状态结束,这可能导致崩溃,或者要求您编写一些补偿代码以避免崩溃
- 更重要的是,您不能仅仅通过阅读产品代码来对产品进行推理
SocialSecurityNumber.TryParse(String untrustedString, out SocialSecurityNumber)
或
UI可以使用这些方法来验证用户输入。UI没有理由临时“破坏”对象。如果你让这种情况发生,你最终会得到。不幸的是,internet上的许多示例代码都促进了这种方法。底线是,你的验证规则来自你的域,应该由域o强制执行对象。首先,
SocialSecurityNumber.TryParse(String untrustedString, out SocialSecurityNumber)
SocialSecurityNumber.IsStringValid(String untrustedString)