Previously we've looked at DDD Entities, that have state, and Events, where state changes. To reduce complexity we can be specific about what has state and encapsulate where it changes. Events are high level code, sitting in the middle of the Onion Architecture. In this article, we will look at how lower level commands link the user interface or API with events to enable users to change state.
Domain events exist in the high level Domain centre of an Onion Architecture diagram, covered by Robert C. Martin in Clean Architecture. The outer layer of the onion contains low level details, inputs and outputs such as storage, user Interfaces, and APIs. Commands are somewhere in between, linking the inputs to the higher level domain so that users can trigger the events to change state.
For a user to interact with a system, and for it to be useful, we need to be able to do two things. First is to display information to the user. To do that we need to read the information from wherever it is stored. Secondly, in most systems the user can update information, so the system needs to write the information to wherever it is stored. Reads are simple, they are easy. Several users can read information at the same time without much processing power or risk of conflicts. Writing is more complex and process intensive, with the risk of conflicts as multiple users write data at the same time. As we saw with Entities and Values the complexity of a system is where state changes, or the writes, and to keep things simple with reads and writes, we can segregate our Queries (reads) from our Commands (writes) to avoid introducing Accidental Complexity and make maintenance easier.
In Clean Code, Robert C Martin talks about how, to avoid unexpected behaviour in code, methods that return a value should not change the values of variables, and methods that change the values of variables should not return anything. CQRS is a similar concept from a less granular, higher architectural level.
Commands Are Complex
Commands are complex, mainly because they trigger Events, which is where state changes. Because of the complexity, we really need to follow good practices. I'd expect something like the Command pattern being followed to link the user doing something in a user interface, or an API call coming in, to the Event. The command code could exist in it's own assembly referenced by your web application. The web app would create a Repository which accesses the database, all of which would live in the outer Details lowest level layer of the Onion Architecture. The Repository will implement an interface defined in a higher level layer of the Onion to ensure Dependency Inversion, injecting the Repository implementation so the Event can use it to persist state. Events themselves will use the core domain Entities and Values to carry out actions and ensure valid state before persisting. To complicate things more, we may have Policies that trigger other commands when an Event occurs, ending up with a chain of complex commands.
If this sounds complex, its because it is. Lets look at how it might look represented in the Onion Architecture:
Queries Are Simple
Queries, on the other hand, used to populate ReadModels that are just displayed in the web application are incredibly simple. All they need to do is go to the database, grab data and display it. Because our Commands, and ultimately Events, ensure state is valid when it is saved, we don't need to worry about the consistency of our data on reads. That complexity is in the Commands and Events. For Queries, we don't need the overhead of going through a Repository, injecting dependencies or using domain Entities and Values. We go straight to the DB from the web app and display the values that come back from the DB, loaded up into a POCO. I'd put the code for Queries into one static class that I would call behind the web app's controller actions, but that's the only encapsulation I would use for Queries.
If this sounds simple, that's because it is. In the Onion Architecture, we can see how simple it is. Queries bypass everything and go straight to the DB to avoid the complexity of state change. This segregation makes the complex state change code simpler as well as it isn't diluted with code that doesn't need to be there.
Unit Testing and CQRS
What does this mean for Unit Testing? If we are going straight to the DB within the web app for Queries and we aren't using Dependency Injection, how can we Unit Test the Queries?
The big question here I think is do you want to test the Queries?
Commands trigger Events which change state, they are complex and should absolutely be Unit Tested mocking out any Repository dependencies so the tests don't hit the DB or any APIs. Similarly, the Entities and Values should be unit tested as well, as they will contain validation and business critical rules. Queries though just return POCOs from the DB. There really isn't anything there to Unit Test.
Commands are complex as they lead to Events which change state. We need to use good patterns and practices when implementing commands, including Commands, Repositories, Dependency Inversion, Unit Testing and Mocking.
Queries are simple, they just grab data from the DB and make it visible in a web app, or return it via an API.
To minimise the complexity and overhead in code, it can be good to segregate the complex Commands from the simple Queries.
Because they are complex, its good to Unit Test Commands. Queries are so simple there is no need to Unit Test them.
- Hands-On Domain-Driven Design with .NET Core: Tackling complexity in the heart of software by putting DDD principles into practice by Alexey Zimarev
- Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
- Clean Code by Robert C. Martin
- Clean Architecture by Robert C. Martin