From CRUD to CQRS. Part1. CQRS
I often read, “You should divide your code into commands and queries” and I ask myself, “Why is my CRUD model bad? Why should I try CQRS? My code works well, I think”. If you ask yourself, “Does your code work well and is it ready for fast feature implementation” and the answer is ”maybe”, I have bad news: you should refactor it.
One day you will understand that your product is hard to scale and there is a lot of code without unit tests. While reviewing the code you will see that your querying model and editing model look like spaghetti code. It is hard to write unit tests for big methods where there are a lot of custom queries (transaction scripts are not bad, but when methods are very big, you will have a problem). Also it is very expensive to maintain these God-classes (e.g., repositories). I just would like to remind you that God-class is an anti-pattern because it violates Single responsibility principle.
You can migrate queries into class (QueryObject) or design expression style repository (migrate expression queries into helper or something else).
You can use different patterns/rules/tools for refactoring of your existing legacy code to make your application better and one day you will find one powerful fundamental principle from million of ideas — this is CQS.
CQRS is more than a pattern, it is architectural thinking. You divide your application in your mind into two parts: Commands and Queries. Forget about CRUD.
The underlying of CQRS is Command–query separation (CQS) principle. The idea of CQS is described by Bertrand Meyer in his book “Object Oriented Software Construction”.
The idea is to divide an object’s methods into two separated categories:
Queries: just return result (last state of system) and don’t provide side effects
Commands: change state of a system and don’t return result (but may be they can).
Command should not return state because from architecture perspective it will migrate to CRUD. Also it gives you more flexibility because Read/Write models are independent and you can change them separately.
There are many decisions about “Should commands return result or not?” Command can return result, but this result can not change state or use for changes another domain objects. It’s important! return result can be like: IsSucess or IsNotSuccess or error list, validation errors and that’s all. For example, command handler call domain validation, and there are 2 ways to handle validation errors: throw exception or using try-catch and return list of validation errors. What would you prefer? In general, commands should not return state of system! Because it will transform your application to CRUD style.
CRUD:
This classic CRUD application model. The data Flow in application will be like this:
All business services and domain services get or set data in one flow.
CQRS:
This is CQRS application model. You see two flow (sides): Read and Write:
Write (commands) flow designed for create/update/delete domain models.
Read (queries) from designed to get data from data source:
Classic CQRS make proposal to divide whole application into Read and Write flow.
“By separate models we most commonly mean different object models, probably running in different logical processes, perhaps on separate hardware. ”- Martin Fowler. But if you are looking for another realization, you will find a lot of variants for implementation of CQRS.
Below there is the first classic example where application is fully separated into 2 sides: Read and write:
The second example where application has common Web Api controllers but business services are separated into 2 sides: Read and write:
And there is the third option where controller and business are common and command and queries are created in common services:
P.S. You see that migration to CQRS is time-consuming and hard work. There are a lot of disadvantages to migrate your current project to CQRS. When should you not migrate to CQRS?
- If you have small project and it works well.
- You have one of the problems in code and you can fix it with standard methods/tools/principles. For example, queries from repository migrate to static class, refactoring business services for each responsibility, move from anemic model to reach model… There are a lot of techniques to make your code better without CQRS!
When should you migrate to CQRS?
- When you work on design of a new project or refactor an old project (it takes much time), you want to fix a complex problem and you really understand the idea of CQRS. You realize that to divide application into Read/Write flow (Commands/Queries) you need to write a lot of classes. Also you really need Event Sourcing. CQRS+Event Sourcing will give you history from the box, and there is another database for reading. It can be MongoDB or another low cost and fast database or you can store data in-memory databases and replace many caching methods in the application.
In the next articles I will write about Event Sourcing and how to integrate CQRS with it.