When to validate commands in CQRS?

I’m continuing the series about CQRS to supplement my recent CQRS in Practice course. There’s a question that I didn’t cover in the course and that was raised at least twice since then: when to validate commands in CQRS?

CQRS commands, command handlers, and validation

In a typical CQRS application with a single database, the write side looks like this:

CQRS write side
CQRS write side
  • The client sends a request to the API (the server). The request contains data that is represented by a DTO (data contract).

  • The server routes the request to a controller which then transforms the incoming DTO into a command and dispatches it to a command handler.

  • The command handler is where the application logic resides. It retrieves necessary data from the database, delegates decision-making to domain classes, and persists the results of those decisions back to the database.

  • Finally, the client receives an acknowledgment: an indication of either success or failure.

So, where in this picture does validation lies? When exactly should you validate a command before acting upon it?

When it comes to preliminary validation, such as checking for non-nullability and such, you could put precondition checks into commands themselves:

public sealed class EnrollCommand : ICommand
{
    public long StudentId { get; }
    public string Course { get; }
    public string Grade { get; }

    public EnrollCommand(long studentId, string course, string grade)
    {
        if (course == null || grade == null) // Precondition checks
            throw ArgumentException();
        
        Id = id;
        Course = course;
        Grade = grade;
    }
}

This could help avoid transmitting invalid commands to command handlers and follow the fail fast principle. You can even introduce a static factory method returning a Result instance, similar to Value Objects:

public sealed class EnrollCommand : ICommand
{
    public long StudentId { get; }
    public string Course { get; }
    public string Grade { get; }

    /* ... */
    
    public static Result<EnrollCommand> Create(long studentId, string course, string grade)
    {
        /* Validate, return either success or failure */
    }
}

Would it be a good idea?

To answer this question, we need to revisit what a command is. A command is a message that tells your application to do something. The application can either accept or reject this message, depending on the application’s current state. There’s no guarantee that the application will go ahead and execute the command.

This is one of the differentiating factors between commands and events. Unlike a command, a domain event represents a fact that already happened and the application can’t do anything about it. It can either take it into account or ignore, but not change or reject it.

Because a command represents things the client asks of your application, that could be anything, including something invalid. Therefore, commands shouldn’t have any invariants attached to them. The application, in turn, is free to reject an invalid command. It means that a command should be a plain bag of properties with no validations:

public sealed class EnrollCommand : ICommand
{
    public long StudentId { get; }
    public string Course { get; }
    public string Grade { get; }

    public EnrollCommand(long studentId, string course, string grade)
    {
        // No invariant checks
        Id = id;
        Course = course;
        Grade = grade;
    }
}

So where to do the validation then? There are a couple of options: either before dispatching the command (in the controller) or after (in the command handler).

Ideally, all validations should be done after the command dispatch. You want to keep the controller as thin as possible - it should only be responsible for the built-in ASP.NET functionality (such as routing) and converting DTOs into commands. Everything else should go to the domain model or commands handlers. This will help you keep a good separation between ASP.NET-related and application-related concerns.

Even trivial validations, such as checking for nulls, field lengths and so on, should go to the domain model in an ideal case. That’s because the information regarding what constitutes a valid email, course name, or student address is part of the domain knowledge and should be kept in the core domain layer (preferably in value objects).

The drawback of this approach is that it’s quite daunting, especially in projects without much complexity. In such simple projects, it may be an overkill to create value objects for each concept in the domain model, such as Email, CourseName and so on. (Check out this article to decide when to create a value objects: Value Objects: when to create one? )

The simpler approach is to rely on the built-in validation mechanism, such as ASP.NET attributes, or tools like FluentValidator. In such a scenario, you will validate commands before they get to command handlers. Which is not ideal from a pureness perspective, but is fine overall if you think that the simplicity gain is worth it. I wrote about a similar topic in this article: Validation and DDD .

Note two things:

  • Even if you take the simpler approach (attributes over value objects), complex validations (such as checking for student’s email uniqueness) should still be done in the command handlers.

  • And if you have an HTML client, you will still need to duplicate the validations on the UI, at least the simple ones, for better user experience. No one wants to wait for a server roundtrip just to see that the email they entered is invalid.

Summary

  • Commands should have no invariants attached to them. The server is free to reject an invalid command.

  • Ideally, all validations should reside either in command handlers or in the domain model. The drawback of this approach is its complexity.

  • In simpler projects, use attributes to streamline the validation. This approach is not as pure, but it’s simpler.

Subscribe


I don't post everything on my blog. Don't miss smaller tips and updates. Sign up to my mailing list below.

Comments


comments powered by Disqus