Thursday, October 25, 2007

Specifications versus validators

Joe posed a great question on my recent entity validation post:

I question the term Validator in relation to DDD.  Since the operation of the Validator seems to be a simple predicate based on business rule shouldn't the term Specification [Evans Pg227] be used instead?

On the surface, it would seem that validation does similar actions as specifications, namely that it performs a set of boolean operations to determine if an object matches or not.

For those unfamiliar with the Specification pattern, it provides a succinct yet powerful mechanism to match an object against a simple rule.  For example, let's look at an expanded version of the original Order class on that post:

public class Order
    public int Id { get; set; }
    public string Customer { get; set; }
    public decimal Total { get; set; }

A Specification class is fairly simple, it only has one method that takes the entity as an argument:

public interface ISpecification<T>
    bool IsSatisfiedBy(T entity);

I can then create specific specifications based on user needs.  Let's say the business wants to find orders greater than a certain total, but the total can change.  Here's specification for that:

public class OrderTotalSpec : ISpecification<Order>
    private readonly decimal _minTotal;

    public OrderTotalSpec(decimal minTotal)
        _minTotal = minTotal;

    public bool IsSatisfiedBy(Order entity)
        return entity.Total >= _minTotal;

Specs by themselves aren't that useful, but combined with the Composite pattern, their usefulness really shines:

public class AndSpec<T> : ISpecification<T>
    private readonly List<ISpecification<T>> _augends = new List<ISpecification<T>>();

    public AndSpec(ISpecification<T> augend1, ISpecification<T> augend2) 

    public AndSpec(IEnumerable<ISpecification<T>> augends)

    public void Add(T augend)

    public bool IsSatisfiedBy(T entity)
        bool isSatisfied = true;
        foreach (var augend in _augends)
            isSatisfied &= augend.IsSatisfiedBy(entity);
        return isSatisfied;

Adding an "OrSpec" and a "NotSpec" allows me to compose some arbitrarily complex specifications, which would otherwise clutter up my repository if I had to make a single search method per combination:

OrderRepository repo = new OrderRepository();
var joesOrders = repo.FindBy(
    new AndSpec(
        new OrderTotalSpec(100.0m),
        new NameStartsWithSpec("Joe")

Given the Specification pattern (fleshed out into excruciating detail), why can't I compose validation into a set of specifications?  Let's compare and contrast the two:


  • Matches a single aspect on a single entity
  • Performs positive matching (i.e., return true if it matches)
  • Executed against a repository or a collection
  • Can be composed into an arbitrarily complex search context, where a multitude of specifications compose one search context
  • "I'm looking for something"


  • Matches as many aspects as needed on a single entity
  • Performs negative matching (i.e., returns false if it matches)
  • Executed against a single entity
  • Is intentionally not composable, a single validator object represents a single validation context
  • "I'm validating this"

So although validation and specifications are doing similar boolean operations internally, they have very different contexts on which they are applied.  Keeping these separate ensures that your validation concerns don't bleed over into your searching concerns.


Gary said...

Hi Jimmy,

Great series of posts on Validation and the Visitor / Specification Patterns.

I'd disagree with your comment that a Specification "Matches a single aspect on a single entity"... surely Composite Specifications were born to handle matching multiple aspects on a single entity? Your example seems to show this?

Jimmy Bogard said...

Excluding the composite specs (AndSpec and OrSpec), each spec performs one predicate match. One validator performs as many predicate matches as satisfies the validation.

Specs can be composed into complex specs, but individual spec types only do one match.