Business rule validators and command handler in CQRS

It’s important to know that commands can be rejected after they are sent to the handler.

At the very least, you could encounter a concurrency violation that cannot be detected until the aggregate root is touched.

But also, the validation that can occur outside of the entity is simple validation. Not only string lengths, numeric ranges, regex matching, etc. but also validation that can be reasonably satisfied through a query or view, like uniqueness in a collection. It’s important to remember that validation involving a materialized view is likely to be eventually consistent, which is another reason a command could be rejected from the aggregate, inside the command handler. That said, to preempt the situation, I often use read models to drive UI choices which only allow valid actions.

Validation that cannot occur outside an entity is your business logic validation. This validation is dependent upon the context in which it is run (see Udi Dahan’s Clarified CQRS).

Business logic should not be in a separate validation service. It should be in your domain.

Also, I’m of the opinion that validation which occurs in the UI should be re-checked not in the command handler, but in the domain too. That validation is there to prevent corruption to the domain — if it is not performed outside the domain then the domain is still subject to invalid parameters.

Using command handlers to duplicate this validation is only a convention. If no other front end is sending commands, then it is a useless duplicate. If there are multiple front ends, it is just one choice of where to place the then-necessary duplicate validation, and in those cases I prefer to handle it in the domain.

Lastly, you will need to bubble up commands rejected from within the handler. I accomplish this with exceptions as much as possible.

Leave a Comment