Command Query Responsibility Segregation Pattern
Disclaimer: This article was written by me as a reference guide for myself. There may be certain parts of the discussion I gloss over and I can guarantee you that this will be changed over time. If you’re interested in this topic, I highly recommend reading the articles listed at the bottom of the article.
In many areas of programming the concept of separation of concerns appears all over the place. In MVC architecture we separate out domain.business logic from the visualization of data through the layers.
This idea of specialization or separation of concerns is extended into the structure/design of our applications through something called the Command Query Responsibility Segregation Pattern (CQRS).
CQRS
In our application, there may be scenarios where it may make sense to separate CRUD operations on a data store. By data store, I mean anything something like a SQL database.
Why would one ever want to perform separate out certain CRUD operations?
- Speed - if we can separate out how (and where) we read and write, there’s potential for speed and scalability improvements in certain scenarios.
- Clarity of model and crystal clear responsibilities
- if we separate our operations, it gives us (the devs) clarity on what a method should do and more importantly what a method should not do (more on this in a bit)
The CQRS patterns enables developers to split out the mental models of queries and commands by splitting up the models that do that work.
Doing any sort of reading on CQRS you start seeing things like splitting up DBs, evaluating the staleness of data and what users see etc. etc. etc.. Before diving into that sort of complexity, if we assume that we’re working with a simple SQL DB. The CQRS pattern enables us to see that by doing something a simple as splitting our read and write methods into interfaces, we’ve got a good method of knowing what the given method’s purpose is (queries or tasks).
To crystalize the idea a bit further, microsoft makes the following recommendations on their articles on the CQRS pattern.
CQRS separates reads and writes into different models, using commands to update data, and queries to read data.
- Commands should be task-based, rather than data centric. (“Book hotel room”, not “set ReservationStatus to Reserved”).
- Queries never modify the database. A query returns a DTO that does not encapsulate any domain knowledge.
Before continuing - it’s worth mentioning that a DTO is a super simplified representation of a result. Think about a scenario where you have a class representing an object. A DTO is the simplest data structure required to represent the information you need to send across the wire to the person asking for the information.
Making CQRS more complicated
I mentioned earlier that the benefit of CQRS was potential speed and scalability improvements. By separating out our two models, this allows us to simply swop out where we perform operations.
As an example, suppose we’ve got an application with a high volume of reads and writes. To optimize for many reads, we’d index our tables to improve read speeds. Doing so reduces write speed - oops.
One solution to this is using CQRS. To preserve the speed of the reads and the writes, we’ll create two DBs, one for reading and the other for writing. In a sense, we’re taking a mental split for queries and tasks and turning it into something very concrete. Splitting out the DBs allows us to optimise the one for reading and the other for writing.
The problems we introduce
CQRS is a brilliant solution for scenarios where you want clients executing read commands with blistering speed. However, if we have a system where we need to read our own writes (e.g. customer performs a command and expects it to reflect immediately), we’ve really introduce a massive headache to our software as we now need to consider how recent the data is that we are reading from (and all the other bits of consistency-based goodness that go along with distributed databases)
When to use CQRS
- According to Fowler, based on his observations, you should look to implement CQRS on a part of a system as opposed to the entire system.In DDD terms you’d use a bounded context as opposed to a system.
Benefits of CQRS
- In certain scenarios it allows breaking up a complicated system into more manageable chunks/operations.
- According to this report, if you don’t need to respond to writes immediately a queueing mechanism works well to queue up commands to execute.
Drawbacks of CQRS
- In some scenarios, the two models may work quite closely together in code and separating out the models may not make sense (Fowler)
- As mentioned, we may introduce the complexity of data consistency
Further Reading
Found this content useful? Consider buying me a coffee.