Series link here.
Update: added an 'addendum' here.
As a reminder of what I’m talking about, here’s the picture from Mark:
What I’m going to be talking about is section #2 from Mark’s picture, and in particular, I’m going to be going over a number of concepts including:
- The differences and advantages of using Commands over DTOs
- Validation of commands, and how/when this can occur outside of the domain
- Why domain objects shouldn’t have getters and setters and how commands help here
- Why domain objects should never be invalid and how commands help here
- UI Implications of using Commands, bye-bye Excel screens!
- The command store – the attentive reader will notice that in section #3 of Mark’s picture, there is something called the Event Store. An improvement, IMO, is to add something called the Command Store, which would have lines coming from the Command Bus to it. At some point, I will produce my own picture/diagram, but for now, just mentally put it in.
A lot of what I’m going to be talking about applies to what I’ve called elsewhere the ‘strict’ version of cqrs, that is, when you simply separate your read and write services, using queries on the reads and commands on the writes. Let’s get to it.
Why commands are better than DTOs
That’s a blanket statement, and so, of course, that means that someone can come up with scenarios where it isn’t true. But I’m more and more convinced that those scenarios will be fewer than scenarios where the statement is true. Let’s consider why this might be the case.
We are on a UI page that allows the user to update something. Let’s suppose it is a Customer Address. A typical way of doing this without cqrs is to create something like a Customer DTO with the updated Address information and pass it into the domain, using some method like CustomerDomainObject.UpdateCustomer(CustomerDTO dto). This works, BTW.
Imagine for a moment that it doesn’t work, and you are trying to figure out why. If it doesn’t work in the sense of throwing an exception, you at least have some idea of where in your code that it failed. That’s good.
Suppose that the reason why it doesn’t work is because some other user has already updated the Customer’s credit rating, and so there is a concurrency conflict (if you are using DTOs, you need to be doing this). Depending on how sophisticated your auditing processes are, you may be able to trace down that this is, in fact, what happened. More likely than not, it won’t be easy, but let’s suppose you can. That’s good.
But, let’s step back and think about this basic scenario. One user was trying to update a Customer Address. The other user was trying to update the same Customer’s credit rating. What business concurrency conflict occurred here? None (leaving aside the possibility that changing a Customer’s Address affected their credit rating). There is no business reason why there should have been a conflict here.
So, why was there a conflict? A generic DTO, passed into a generic UpdateCustomer(CustomerDTO dto) method has no understanding of context. When done right, DTOs use a timestamp value to prevent concurrency conflicts, but here you have two different and non-conflicting business requests that are failed due to a technical restriction. There is no reason why both of these requests couldn’t have been honored otherwise.
Commands can help here. Instead of sending a generic DTO to a generic UpdateCustomer method, send a Command that specifies exactly what it is trying to update. Send an UpdateCustomerAddress command to your domain, along with a UpdateCustomerCreditRating command to your domain. Since they are trying to do two separate things, you don’t have to fail one just due to concurrency issues.
More importantly, don’t send rather generic UpdateCustomerAddress and UpdateCustomerCreditRating commands. Instead, send more specific CustomerMovedChangeAddress and ReduceCustomerCreditRatingDueToLatePayment commands (or whatever) to your domain. Specify *exactly* what you are trying to achieve.
Why? Because then you can isolate whether those commands should succeed or fail due to considerations that relate specifically to the context in question.
Now, if you’ve used DTOs before, you might be appalled at the implications of this. Having a single DTO seems to be a good use of code reuse. Using commands instead of DTOs seems to mean that you end up writing more code. The short answer is, yes, you do have to write more code.
But keep in mind how your code is used, past the point of it being developed, and when your operations team is dealing with it. Which is easier, figuring out why some generic UpdateCustomer(CustomerDTO dto) method failed, or why some ReduceCustomerCreditRatingDueToLatePayment command failed?
More on this below.
There’s validation and then there’s validation
When determining whether a command is valid or not, there are different levels of validation that you need to consider. A lot of validation can be done at the UI layer before a command ever makes it to your domain. At the command level, you can handle the sort of validation of user inputs that any typical application needs. Is this string level of the right length, and of the right pattern, blah blah blah. The command handler of each command can handle these sorts of validations, and provide quick response to the UI of any issues, before you ever hit the domain.
Validation against business rules and context happens at your domain level, which is section #3 of Mark’s picture. Think of the difference between a validation rule that a customer address must be less than 150 characters in length versus a validation rule that says you cannot degrade a customer’s credit rating, even due to late payment, if they process more than $100k in business a month.
Domain objects shouldn’t have getters or setters
For the longest time, I heard about this, and didn’t get it. I came from a background of using DTOs and managing what I considered to be domain objects, and thought this idea was mystifying, even insane. How could I possibly manage something as central as a Customer domain object, if I couldn’t use getters and setters?
The use of a query layer explains how to rid domain objects of getters, since you don’t hit your domain objects at all. You hit your query layer, which hits your reporting store, which returns screen/task specific objects that gives you all of the information you need. Your domain doesn’t play any role in it.
But what about setters? How could you update a Customer object to have the correct Address for shipping product if you didn’t allow setters? Crazy.
But once you start to think about the difference between using Commands and DTOs, it starts to make sense. A command that says CustomerMovedUpdateAddress gets passed (eventually) into the domain, which then processes that command internally. You don’t need public get/set on Address1, get/set City, get/set Country, etc. on each property of your domain object. You just need a public method that can handle the command that is telling you the set of changes that you want to process. Which leads to….
Domain objects should never be invalid
Suppose you passed in a CustomerMovedUpdateAddress command that had an inconsistent City and State combination. If you handle this sort of validation at the command level, this would never occur, but let’s suppose it did. Your domain object should have the logic internally to accept or reject all of those change as a batch.
If you allow changes to the internal state of your domain object on a property by property basis, then you have a heck of a lot of validation logic you have to write to ensure that your domain object is never in an invalid state. If you encapsulate all of those changes in a command object, you can accept or reject those changes as a whole, ensuring that your domain object is never put into an invalid state. Whether it is changing the Address or the CreditRating, or whatever, the use of commands allows you to accept or reject a set of changes as a batch, and thus your domain object is never put into an invalid state.
No more Excel-like screens
A lot of Microsoft demos show you a big grid of values where you can select a row and make some changes and then click an update button. This works, BTW, but has a lot of problems, which I’ve been trying to describe here. Implicitly or explicitly, they work under the covers like the common DTO pattern I’ve described. Batch a whole set of changes without context, pass them to where ever they need to be processed, and hope you don’t get a concurrency error.
There’s no easy solution here, except to try and make your UI better, more attuned to the specific tasks you want to achieve. This is really hard. The Excel-like grid screen is common place, easy to produce, and hard to get away from. But it is important to do so if you want to produce a scalable application that works across multiple users.
The Command Store
Few discussions of cqrs talk about having a separate command store, but I think it is important.
As I’ve talked about in other places, code exists long after it is written, and most importantly, it exists in situations where it has to be maintained. One of the reasons why I think it is important to choose commands over DTOs is that you have a record of specific actions taking place. An UpdateCustomer(CustomerDTO dto) method tells you nothing, but a method that tells you that you sent a ReduceCustomerCreditRatingDueToLatePayment command tells you something.
If you have a store that saves every command that is sent into the system, you have a store that gives you an automatic audit trail of what the users have tried to do. Constructed properly, you have a trail of everything that has attempted to affect you system.
With this record, you can troubleshoot your production system. With this record, in theory, you can recreate the record against your UAT system, or against a new DEV system.
Anyone who has worked in an Operations environment should be able to think about ways in which this works really well.
A lot of the fun stuff comes in discussing section #3 of Mark’s picture, which I hope to talk about soon.