您如何处理域驱动设计中复杂聚合的验证?您是否整合了业务规则/验证逻辑?
我理解参数验证.我理解可以附加到模型本身的属性验证,并执行检查电子邮件地址或邮政编码是否有效或者名字具有最小和最大长度等操作.
但是涉及多个模型的复杂验证呢?您通常将这些规则和方法放在您的架构中?你用什么模式来实现它们?
不要依赖于IsValid(xx)
整个应用程序的调用,请考虑从Greg Young那里获得一些建议:
不要让您的实体进入无效状态.
这基本上意味着你从实体的思维转变为纯数据容器,更多的是关于具有行为的对象.
考虑一个人地址的例子:
person.Address = "123 my street"; person.City = "Houston"; person.State = "TX"; person.Zip = 12345;
在任何这些调用之间,您的实体是无效的(因为您将拥有彼此不一致的属性.现在考虑这个:
person.ChangeAddress(.......);
所有与改变地址行为有关的调用现在都是一个原子单元.您的实体在此处永远不会失效.
如果你采用建模行为而不是状态的想法,那么你可以达到一个不允许无效实体的模型.
有关此问题的详细讨论,请查看此infoq访谈:http: //www.infoq.com/interviews/greg-young-ddd
我喜欢Jimmy Bogard解决这个问题的方法.他在他的博客上发表了一篇名为"访问实体验证和扩展方法"的文章,其中他提出了一种非常优雅的实体验证方法,建议实现一个单独的类来存储验证代码.
public interface IValidator
{
bool IsValid(T entity);
IEnumerable BrokenRules(T entity);
}
public class OrderPersistenceValidator : IValidator
{
public bool IsValid(Order entity)
{
return BrokenRules(entity).Count() == 0;
}
public IEnumerable BrokenRules(Order entity)
{
if (entity.Id < 0)
yield return "Id cannot be less than 0.";
if (string.IsNullOrEmpty(entity.Customer))
yield return "Must include a customer.";
yield break;
}
}
我通常使用规范类,它提供了一种方法(这是C#,但你可以用任何语言翻译它):
bool IsVerifiedBy(TEntity candidate)
该方法对候选者及其关系进行全面检查.您可以在规范类中使用参数使其参数化,就像检查级别一样......
您还可以添加方法以了解候选人未验证规范的原因:
IEnumerableBrokenRules(TEntity canditate)
您可以简单地决定实现第一个方法,如下所示:
bool IsVerifiedBy(TEntity candidate) { return BrokenRules(candidate).IsEmpty(); }
对于破坏的规则,我通常编写一个迭代器:
IEnumerableBrokenRules(TEntity candidate) { if (someComplexCondition) yield return "Message describing cleary what is wrong..."; if (someOtherCondition) yield return string.Format("The amount should not be {0} when the state is {1}", amount, state); }
对于本地化,您应该使用资源,以及为什么不将文化传递给BrokenRules方法.我将这些类放在模型命名空间中,其名称建议使用它们.