Validation in DDD and CQRS

One of the most common questions about Domain Driven Design and CQRS is "where" to put the validation logic for our entities. This is probably not the right question, it would make more sense asking "when" we should put the validation.

The main difference between an "anaemic" and a "domain" model is that our objects do not contain just data but also behaviours. Encapsulation is the key value when we talk about DDD.
As per its definition encapsulation guarantees the integrity of the data contained in the object.
So why do not put the validation inside the aggregate root?
Why do not put the logic inside the method that is changing the state of the object?
The state of the object should change only if the new values are valid.

If we follow this principle we will stop asking ourselves if the object is valid or not and we will probably start asking if we can perform that operation. Can I change the value of the properties with the data I'm receiving from the UI?

Let's assuming we have the following User aggregate root:

public class User : IAggregateRoot  
{
    public Guid Id { get; private set; }
    public string Email { get; private set; }
}

To create a new user we need an id and an email address. In a typical CQRS scenario if we want to create a new user we should have a command similar to this:

public class CreateUser : ICommand  
{
    public Guid Id { get; set; }
    public string Email { get; set; }
}

Now we need to validate this command (the action we are trying to perform), and to do that we need a validation mechanism. Instead of creating our own validation engine we can use the excellent open source library FluentValidation.

What follows is an example of a validator for the CreateUser command.

public class CreateUserValidator : AbstractValidator<CreateUser>  
{
    private readonly IUserRepository _repository;

    public CreateUserValidator(IUserRepository repository)
    {
        _repository = repository;

        RuleFor(c => c.Id)
            .NotEmpty().WithMessage("User id is required.");

        RuleFor(c => c.Email)
            .NotEmpty().WithMessage("Email is required.")
            .EmailAddress().WithMessage("Email not valid.")
            .Must(HaveUniqueEmail).WithMessage("Email already used.");
    }

    private bool HaveUniqueEmail(string email)
    {
        return _repository.GetByEmail(email) == null;
    }
}

As you can see we could even use a repository to validate the uniqueness of the email address without injecting that repository in the domain object. Now we can add a method to the aggregate root that we can use to pass the command and to inject the validator.

public class User : IAggregateRoot  
{
    public Guid Id { get; private set; }
    public string Email { get; private set; }

    private User(CreateUser cmd)
    {
        Id = cmd.Id;
        Email = cmd.Email;
    }

    public static User CreateNew(CreateUser cmd, IValidator<CreateUser> val)
    {
        val.ValidateAndThrow(cmd);

        return new User(cmd);
    }
}

We call the ValidateAndThrow extension method of the validator that performs the validation and throws an exception in case of errors. Only after the validation has passed we create a new User object assigning the values passed with the command.

What's left? An handler for the command with the dependencies injected:

public class CreateUserHandler : ICommandHandler<CreateUser>  
{
    private readonly IUserRepository _repository;
    private readonly IValidator<CreateUser> _validator;

    public CreateUserHandler(IUserRepository userRepository, 
        IValidator<CreateUser> validator)
    {
        _userRepository = userRepository;
        _validator = validator;
    }

    public void Handle(CreateUser command)
    {
        var user = User.CreateNew(command, _validator);

        _repository.Create(user);
    }
}

Similar scenario if we want to perform any kind of actions in our objects:

public void ChangeEmail(ChangeEmail cmd, IValidator<ChangeEmail> val)  
{
    val.ValidateAndThrow(cmd);

    Email = cmd.Email;
}

Working examples of this pattern in my open source CMS project called Weapsy: https://github.com/weapsy/weapsy

comments powered by Disqus