The Command and Query Responsibility Segregation (CQRS) pattern separates read and write operations for a data store. Reads and writes may take entirely different paths through the application and may be applied to different data stores. CQRS relies on asynchronous replication to progressively use writes to the read view so that changes to the application state instigated by the writer are eventually observed by the reader.
How CQRS Works
Consider, for a moment, a run-of-the-mill rideshare service. Traditionally, the client apps would query the central database for drivers and their locations. At the same time, drivers would send commands to the central database to update their locations in real-time. The database queries would crosscheck for driver and user locations and respond to the client apps and drivers accordingly. These sorts of queries can put an enormous strain on the database. Worse, they must execute in near real-time; otherwise, the user experience degrades noticeably.
In a traditional, N-Tier application model, queries and commands are handled in the same vein. By and large, both reads and writes traverse the same service logic (via controllers, models, etc.) and end up poking the same database, as illustrated below.
This makes the database a giant I/O bottleneck. This is particularly problematic when dealing with relational databases, as the latter are not generally known for their phenomenal scalability. Credit where it’s due: relational databases are excellent for maintaining data integrity, isolating transactions, and applying updates atomically, but they are inherently limited in other ways. They are not “growth-friendly”, to put it mildly.
When To Use CQRS?
- Scenarios where the volume of reading operations is significantly higher than write operations.
- Where one team of developers can focus on the write side and another team can focus on the read side. There is nothing limiting CQRS to a single read-side: multiple independent read projections may coexist, and therefore, multiple teams may be assigned to the task.
- Cases where the access patterns for writing vary significantly from those for reading. For example, transactional writing vs reporting and analytics on the read side.
Benefits of CQRS
- Scalability. CQRS lets us scale reads independently of writes.
- Security. The segregation principle can be applied to information security as well. There is no need for a reader to mutate the read-side state; therefore, the security permissions can be tightened accordingly. This helps enforce the Principle of Least Privilege (PoLP).
- Availability. If the write side goes down, the system will be unable to perform updates; however, users will still be able to see their data. On the other hand, if the read side goes belly-up, the system can fall back to querying the write-side database. (Although this fallback is rarely implemented in practice.)
Challenges of CQRS
- Complexity. There are more moving parts with CQRS. We have a write side, a read side, and typically an event broker or message queue in the middle. We also tend to adopt multiple persistence stacks: often a relational database on the write-side is complemented by a NoSQL product on the read-side.
- Consistency. Due to the asynchrony of CQRS, reads inherently lag behind writes — meaning that the read-your-own-writes consistency is forfeited.
CQRS partitions the internals of your application into a read and a write side, allowing you to design and optimize the two paths independently, it can be very essential as it improves the performance of our application