域驱动devise中的validation

如何处理域驱动devise中复杂集合的validation? 你整合你的业务规则/validation逻辑吗?

我理解参数validation。 而且我了解可以附加到模型本身的属性validation,并执行诸如检查电子邮件地址或邮政编码是否有效或名字的最小和最大长度等。

但是涉及多个模型的复杂validation呢? 你通常把这些规则和方法放在你的架构中? 你用什么模式来实现它们?

我喜欢Jimmy Bogard解决这个问题的方法。 他在他的博客上发表了一篇题为“使用访问者和扩展方法进行实体validation”的文章 ,其中他提出了一种非常优雅的实体validation方法,build议实施一个单独的类来存储validation代码。

public interface IValidator<T> { bool IsValid(T entity); IEnumerable<string> BrokenRules(T entity); } public class OrderPersistenceValidator : IValidator<Order> { public bool IsValid(Order entity) { return BrokenRules(entity).Count() == 0; } public IEnumerable<string> 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; } } 

不要依靠整个应用程序的IsValid(xx)调用,可以考虑从Greg Young那里得到一些build议:

永远不要让你的实体进入无效状态。

这基本上意味着你从实体的思维过渡到纯粹的数据容器,而更多的是关于具有行为的对象。

考虑一个人的地址的例子:

  person.Address = "123 my street"; person.City = "Houston"; person.State = "TX"; person.Zip = 12345; 

在任何这些电话之间,你的实体是无效的(因为你会有不同意的属性,现在考虑这个:

 person.ChangeAddress(.......); 

所有与更改地址行为相关的调用现在都是一个primefaces单位。 你的实体在这里永远不会失效。

如果您将这种行为build模的想法而不是状态,那么您可以达到一个模型,不允许无效的实体。

有关这方面的一个很好的讨论,看看这个infoq采访: http : //www.infoq.com/interviews/greg-young-ddd

我通常使用一个规范类,它提供了一种方法(这是C#,但你可以用任何语言翻译):

 bool IsVerifiedBy(TEntity candidate) 

该方法对候选人及其关系进行完整的检查。 您可以使用规范类中的参数来使其参数化,如检查级别…

你也可以添加一个方法来知道为什么候选人不validation规范:

 IEnumerable<string> BrokenRules(TEntity canditate) 

你可以简单地决定实现这样的第一个方法:

 bool IsVerifiedBy(TEntity candidate) { return BrokenRules(candidate).IsEmpty(); } 

对于破碎的规则,我平时写一个迭代器:

 IEnumerable<string> BrokenRules(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方法。 我将这些类放在模型名称空间中,并使用build议使用的名称。

这个问题现在有点老了,但如果有人感兴趣,这里是我如何在我的服务类中实现validation。

我在每个服务类中都有一个私有的Validate方法,这些方法需要执行一个实体实例和操作,如果validation失败,则会抛出一个自定义exception,并显示违规规则的细节。

示例DocumentService内置validation

 public class DocumentService : IDocumentService { private IRepository<Document> _documentRepository; public DocumentService(IRepository<Document> documentRepository) { _documentRepository = documentRepository; } public void Create(Document document) { Validate(document, Action.Create); document.CreatedDate = DateTime.Now; _documentRepository.Create(document); } public void Update(Document document) { Validate(document, Action.Update); _documentRepository.Update(document); } public void Delete(int id) { Validate(_documentRepository.GetById(id), Action.Delete); _documentRepository.Delete(id); } public IList<Document> GetAll() { return _documentRepository .GetAll() .OrderByDescending(x => x.PublishDate) .ToList(); } public int GetAllCount() { return _documentRepository .GetAll() .Count(); } public Document GetById(int id) { return _documentRepository.GetById(id); } // validation private void Validate(Document document, Action action) { var brokenRules = new List<string>(); if (action == Action.Create || action == Action.Update) { if (string.IsNullOrWhiteSpace(document.Title)) brokenRules.Add("Title is required"); if (document.PublishDate == null) brokenRules.Add("Publish Date is required"); } if (brokenRules.Any()) throw new EntityException(string.Join("\r\n", brokenRules)); } private enum Action { Create, Update, Delete } } 

我喜欢这种方法,因为它允许我将所有的核心validation逻辑放在一个简单的地方。

java世界中,你应该考虑使用hibernatevalidation

对于简单的validation是非常可读的

 public class Car { @NotNull private String manufacturer; @NotNull @Size(min = 2, max = 14) private String licensePlate; @Min(2) private int seatCount; // ... } 

至于复杂的validation。 有一个机制来写你自己的validation。

 package org.hibernate.validator.referenceguide.chapter02.classlevel; @ValidPassengerCount public class Car { private int seatCount; private List<Person> passengers; //... }