Validation in a Domain Managed Project

How do you deal with checking complex aggregates in a domain-driven project? Do you strengthen your business rules / validation logic?

I understand the argument. And I understand the property check, which can be tied to the models themselves, and to do things like checking that the email address or zip code is valid or that the name has a minimum and maximum length.

But what about a comprehensive test that includes several models? Where do you usually place these rules and methods in your architecture? And what patterns, if they are used to implement them?

+51
validation domain-driven-design
Feb 05 '09 at 16:20
source share
5 answers

I like Jimmy Bogard's solution to this problem. He has an entry on his blog called Entity Verification with Visitors and Extension Methods , in which he presents a very elegant approach to entity verification, which offers an implementation of a separate class for storing verification code.

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; } } 
+35
Jun 13 '09 at 13:28
source share

Instead of relying on IsValid(xx) calls throughout your application, consider accepting some recommendations from Greg Young:

Never allow your entities an unacceptable state.

This basically means that you are moving from thinking to objects as pure containers of data and more about objects with behavior.

Consider an example of a person's address:

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

Between any of these calls, your entity is invalid (because you will have properties that are not consistent with each other. Now consider the following:

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

all calls related to address change behavior are now atomic units. Your essence is never unacceptable.

If you accept this idea of ​​modeling behavior, not state, then you can achieve a model that does not allow invalid objects.

For a good discussion of this subject, check out this infoq interview: http://www.infoq.com/interviews/greg-young-ddd

+54
Feb 06 '09 at 22:29
source share

I usually use a specification class, it provides a method (this is C #, but you can translate it into any language):

 bool IsVerifiedBy(TEntity candidate) 

This method performs a full check of the candidate and his relationship. You can use the arguments in the specification class to make it parameterized, for example, the check level ...

You can also add a method to find out why the candidate has not confirmed the specification:

 IEnumerable<string> BrokenRules(TEntity canditate) 

You can simply decide to implement the first method as follows:

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

For broken rules, I usually write an iterator:

 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); } 

You must use resources for localization and why not pass the culture to the BrokenRules method. I put these classes in the model namespace with names that suggest their use.

+6
Feb 06 '09 at 22:21
source share

Now this is a little outdated, but in case anyone is interested here, how do I implement validation in my service classes.

I have a private Validate method in each of my service classes that accepts an entity instance and an action to be taken if the validation fails, a custom exception is thrown with details of the violated rules.

Example DocumentService with built-in 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 } } 

I like this approach because it allows me to put all my basic validation logic in one place, which simplifies the process.

0
Aug 23 '16 at 1:12
source share

In java world you should use hibernation check

It is very readable for simple checks.

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

As for comprehensive checks. There is a mechanism to write your own checks.

 package org.hibernate.validator.referenceguide.chapter02.classlevel; @ValidPassengerCount public class Car { private int seatCount; private List<Person> passengers; //... } 
0
Jun 21 '17 at 7:35
source share



All Articles