CQRS Pattern
Overview
The Command Query Responsibility and Segregation pattern (CQRS) allows operations to be split logically into commands (writes) and queries (reads). This allows for single responsibility and clearly defined code responsibility which can be optimised appropriately, along with the ability to test more easily, along with many other advantages.
- Commands are used to change the state of the data.
- Queries handle complex join operations and return a result, they do not change the data.
In high data volume applications using a single database can cause bottlenecks and adversely affect performance. By using CQRS and event sourcing patterns we separate read and write functionality and can also further separate databases to improve application performance in these situations.
As an example, a monolithic application will not only be saving data where validations and business logic may need to be performed, but also be responsible for complex join operations when reading the data.
This can put a significant load on the database and slow all operations down.
The read operations and write operations each require a different strategy.
By using separation of concerns it is possible to split the process into two databases. The write operations would be performed on the first database, whilst a second database with eventual consistency from the first could provide a set of materialised views which would be used for read operations. In this situation the read database could use a No-SQL database with denormalised data, whilst the write database could use a fully normalised relational database to enforce data consistency.
The approach of two databases does add complexity and can introduce data consistency and synchronisation issues.
Logical CQRS Implementation refers to splitting the command and query operations.
Physical CQRS Implementation refers to splitting the command and query operations along with using physically separate databases.
Most systems would implement Logical CQRS and move to Physical CQRS as and when it becomes necessary.
In .Net applications we commonly use the Mediator pattern (along with the MediatR or similar library) to help implement the CQRS pattern.