Any of these sound familiar?
Rubbish in rubbish out.
Never trust user input.
If you’ve ever built any sort of professional application you will have had to validate user input. Whether it is to ensure a valid email address or something more complicated. There are tried and tested ‘rules’ for doing this safely while being nice the user.
- Validate on the client side - makes for a better user experience
- And validated on the server - never trust user input
But here’s a question..
If you issue commands rather than send in models, where should the validation occur?
The most obvious answer is in the domain model.
But this poses a problem...
Commands are handled by methods with no return type. Just take a look at the
Aggregate Root for an example. Not only that, they are not necessarily processed right away. Before I dive into how to validate your commands, let's just cover how normal validation works. Just to be clear, a CQRS command is not the same as the Command pattern made popular by the GoF. If you are unsure of the difference, take a look at this post
here.
How Validation is Normally Done
Given client side validation cannot be trusted, most developers start validation by ensuring the server side is bullet proof. The user fills in a form and submits a model to the server. The server would then apply a set of logic to determine if the model is valid. The code for this is normally centralised. I.e. all the validation logic is kept together. Importantly all validation is treated as one. There is no separation between superficial type validations like, email or domain concepts like ‘package too heavy for transporter’.
Once the server validation is working it is then applied to the client side for a better user experience.
How Do You Validate Commands?
As I hinted above, the trick is to differentiate between
superficial validation and
domain validation. In short, there are 2 types of validation and they belong in different places.
What is Superficial Validation?
Required fields, email formatting, valid dates are all examples of superficial validation. The key here is to think, what is true regardless of the state of the domain. If a field is required, it is required whether or not the domain model will accept the command or not.
These kind of validations should be done before the command is issued. At least on submission of the form but ideally on the client as well.
FluentValidation Is A Good For Superficial Validation
I’ve found
FluentValidation to be a convenient package for validating commands. And with a bit of convention over configuration, you can save yourself a fair bit of typing.
What is the convention?
Just post fix the name of the class with the validation rules with the name of the class being validated. Simples. Here is how it might look:
public class ImportStressTest : Command
{
public readonly Guid AggregateId;
public readonly int Version;
public readonly Guid UserId;
public readonly Guid ReportId;
public readonly string Data;
public ImportStressTest(Guid aggregateId, int version, Guid userId, Guid reportId, string data)
{
AggregateId = aggregateId;
Version = version;
UserId = userId;
ReportId = reportId;
Data = data;
}
}
public class ImportStressTestValidator : AbstractValidator<ImportStressTest>
{
public ImportStressTestValidator()
{
RuleFor(message => message.AggregateId).NotEmpty();
RuleFor(message => message.Version).GreaterThanOrEqualTo(0);
RuleFor(message => message.UserId).NotEmpty();
RuleFor(message => message.ReportId).NotEmpty();
RuleFor(message => message.Data).NotEmpty();
}
}
Having established the convention you will want to take advantage of this in your internal message bus. Add logic to the message bus which checks for the existence of a validator for any given command. It can then run the validator before issuing the command.
What is Domain Based Command Validation?
This is where the validity of a command is dependant on the state of the model. For example it may be valid to transport package that weighs a ton in one vehicle but not in another. The reason for the difference is found within the domain logic.
It can get a bit confusing here when you have what looks like superficial validation but is actually domain based. For example a field which is required in certain circumstances. E.g. a workflow may require a note of explanation if withdrawing some live data but not if withdrawing draft data. The rule of thumb is - if it needs domain state then it lives in the domain.
As always there are exceptions. Sometimes your validation logic will need data from the wider system to work. This means it’s not superficial validation but it also doesn’t need the domain model. This kind of validation usually sits with an application service. It is typically run before the command reaches the domain.
How do you return the validation errors?
This is a common problem. A key facet of using commands is not having a return type. That is a command, is ultimately handled by a method with a void return type. This leaves us with only 1 option, throw an exception.
Not entirely true, and here’s why…
Working from the domain out to the client, here are 4 ways you could do validation in a CQRS application. The right way for your application maybe 1 of the four or more likely a combination.
Domain Side
- 1) Throw an exception in the domain.
- 2) Publish an event detailing validation problems
Server Side:
- 3) Throw an HTTP error code
- 4) And/Or use a ‘Common Result Object’.
1. Throwing an Exception in the Domain
Throwing a domain side validation exception assumes your commands are handled within the normal flow of the application rather than being queued for later actioning. It doesn’t have to be synchronous but it is much simpler. The problem here is that unwinding the stack to throw an exception can be costly on server performance. You should therefore reserve this approach for truly exceptional cases or low traffic systems.
Here is a simplistic example I stole from my article about
how to build aggregate roots.
public void BookHoliday(BookHoliday command)
{
// Ensure the invariants would not be broken by the command
var totalDaysRequested = (command.EndDate - command.StartDate).TotalDays;
var combinedTotal = totalDaysRequested + _totalDaysTaken;
if (combinedTotal > totalAllocated) throw new NotEnoughHolidaysRemainingException();
// 2nd Phase - Create and commit the event
ApplyChange(new HolidayBooked(command.AggregateId, command.Version, command.UserId, command.StartDate, command.EndDate));
}
2. Publishing an Event Describing the Problem
Publishing an event is rather more interesting. I helped a subscriber of this blog with a problem around how to handle invalid commands. In his case users had to ‘clock-in’ to a location then ‘log work’ done. The problem was the client devices were frequently offline. It was also possible for users to forget to clock-in or out or even log work to the wrong location. Because of the offline issue, commands had to be accepted even when they appeared to be invalid.
The solution was for the domain to raise an event when it detected an issue. The application could then raise a flag for the admins who could call the operator and clarify what was supposed to have been logged.
3. Throwing an HTTP Error Code
You can use HTTP error codes on the server side. Usually a 400 bad request is ideal for validation problems.
But there is another way…
4. Simplify Your Life with the Common Result Object
I first saw something similar when working with the ServiceStack framework. The framework allowed you to add a ResponseStatus to any return object. It had a common shape and would be a good place to add validation failures.
Rob Conery went a step further by suggesting returning a CommonResult from every server request.
But commands are handled by void methods - how do you return a common result?
Just because the methods on your domain object are void, does not mean your HTTP endpoints need to also be void (or 200 ok). By creating a convention that every endpoint which handles commands or queries must return a CommonResult object, you can greatly simplify a whole host of operations including validation.
[Tweet "A CommonResult object can greatly simplify your app."]
The CommonResult object would typically look like this:
public interface ICommonResult
{
dynamic Data { get; }
int TotalResults { get; }
string FlashMessage { get; }
bool Success { get; }
}
This common result object should not contain exception details or stack traces. This is to ensure you do not leak potentially sensitive data.
Hold on - returning data from a command! Isn’t that breaking the basic approach to CQRS?
Think about it…
The domain model still returns void or throws an exception when a command is executed. The distinction here is we can check the superficial validity before the command is ever executed. Just to be clear, the http endpoint returns the CommonResult object when a command is issued. The domain model doesn’t - it's still void. So you can handle superficial validation errors and domain validation errors/exceptions.
Summary
There are 4 places validation takes place in CQRS system.
- Client side
- Server side
- Application service
- Domain Objects
The first two are for Superficial Validations such as required fields etc. The application service is the right place for superficial validations which requires lookup information. Finally, any validation which requires domain logic sits in the domain. Validation failures in the domain do not necessarily need to throw exceptions, events can also be raised for more advanced scenarios.
If you have found this article helpful, please share with your social networks.
You may also like
A common issue I see is understanding the flow of commands, events and queries within a typical CQRS Event Sourcing based system. The following post is designed to clear up what happens at each step. Hopefully, this will help you to reason about your code and what each part does. What is CQRS and what
Read More
Making mistakes is part of programming. Spotting them early can save you time. I’ve started to notice a common set of ‘DDD Mistakes’ which many of us seem to make. And yes, I’ve made them all at some point in my career. This is by no means the definitive list – I’m sure there are
Read More