CQS and CQRS: Twins or Distant Cousins?
When it comes to software engineering, we have a knack for acronyms. And two that often get mixed up are CQS and CQRS. Although might seem similar, they serve different purposes.
When it comes to software engineering, we have a knack for acronyms. And two that often get mixed up are CQS and CQRS.
At first glance, you might think they’re one and the same, akin to twins, but look a little closer and you'll find they're more like distant cousins.
Here's why.
CQS (Command Query Separation): General behavioral principle Coined by Bertrand Meyer in his book 'Object-oriented Software Construction' (1988, Prentice Hall), CQS is a principle asserting that an object's methods should either be Commands or Queries, but never both.
Commands perform an action.
Queries solely return data.
If you consider the codebase to be a bustling city, CQS applies the traffic laws to ensure everything flows smoothly.
It keeps the streets (methods) clear of accidents (unexpected side effects) by preventing the same method from making changes AND broadcasting those changes.
Let's look closer at this traffic management system. Let's say a method is responsible for both registering a vehicle's speed and determining if it's exceeding the speed limit.
This violates the Command Query Separation principle as it combines a side effect (recording the speed) with information retrieval (checking for a speed limit breach).
Let's fix that simply by separating the state-changing behavior from the state-retrieving one.
This separation clarifies the responsibilities of each method, making the code easier to read, understand, and maintain. It also prevents unexpected side effects, which are common in methods that combine querying and state modification. By creating this distinction you’ll be much more confident to mix and match queries tailored to your needs. With commands you’ll still need to be thoughtful.
Now, of course that doesn’t mean we need to be complete purists when it comes CQS, especially when it comes to returning a value from a command.
Exceptions occur, such as when you’re creating a new entity and you return an id. But while commands returning a value is not the end of the world, it’s crucial you don’t alter the state in a query.
All in all, when applied at the method or class level, CQS is all about clarity in design and making your code easier to reason about.
CQRS (Command Query Responsibility Segregation): The Architectural Evolution
CQRS takes the essence of CQS and expands it into the realm of system design.
Here, the game changes.
We're no longer talking about methods. We’re talking abut segregating the entire system into two parts:
one for commands (writes)
one for queries (reads).
Think of your system using two separate databases:
one for reading the data.
one for writing data in.
Why the split?
To allow independent scalability and optimization.
With CQRS, you can scale your read and write workloads independently. You're free to optimize and modify the read side without touching the writes, and vice versa.
It's having two specialists handling what they do best.
Now, of course it’s not compulsory to have two databases in order to apply CQRS. You can begin implementing CQRS starting with a logical separation between your read and update models in your system and grow from there, balancing scalability and simplicity.
Because with great power comes...you guessed it: complexity.
Implementing CQRS isn't a walk in the park.
You're dealing with separate models, tackling consistency concerns, and often venturing into eventual consistency territory.
It's a trade-off, a balancing act between scalability and simplicity.
Event Sourcing: Time Travel Made Possible
CQS:
Mostly on the sidelines for this play.
It doesn’t inherently interact with or leverage event sourcing.
CQRS:
A frequent collaborator with event sourcing.
It elegantly stores changes as a breadcrumb trail of events, allowing systems to rewind, replay, and predict.
Consistency: A Balancing Act
CQS:
Operates in a world where consistency is a given, not a luxury.
It’s all local, straightforward interactions here.
CQRS:
Walks a tightrope with consistency, especially in distributed systems.
Immediate consistency isn't always a guarantee, making way for its more relaxed cousin, eventual consistency.
Integration with Other Patterns:
CQS:
It is a principle that’s more internal and localized.
It doesn't necessarily dictate or heavily influence the broader architectural decisions and is often orthogonal to other patterns.
It’s about making the individual components of a system (the methods) more understandable and manageable, reducing complexity where functionality and data retrieval are concerned.
CQRS:
It’s a top-tier player in system design, often used in concert with other high-level patterns.
It pairs exceptionally well with event sourcing, where the system's state changes are logged as a series of immutable events.
This relationship allows for advanced capabilities, like system snapshots or an audit log with "undo" functionality.
Complexity and Suitability:
CQS:
Given its method-level governance, adopting CQS doesn't require a significant overhaul of the system architecture.
Suitable for systems of various sizes.
It demands a cultural shift in how developers approach writing methods/functions, aiming for pure, side-effect-free functions.
CQRS:
Implementing CQRS is no small feat.
It involves a fundamental restructuring of how the system handles data.
It's ideal for large-scale, enterprise-level applications where scalability, high performance, and reliability are paramount.
Its trade-off is increased complexity in data consistency and transaction management, potentially leading to higher initial development and maintenance costs.
Conclusion CQS and CQRS, though stemming from similar principles, serve two different purposes.
CQS excels in method-level clarity and directness.
CQRS tackles system-wide segregation, scalability, and embraces complexity.
They are not just acronyms, but approaches tailored for specific paths in software engineering.
P.S. If you enjoyed this post, share it with your friends and colleagues.
Great article yet again Helen! I never knew CQS and CQRS are so different. I always assumed CQRS is just a more advanced version of CQS.
One example of a system that needs high scalability but wouldn't necesarrily benefit from CQRS (at least not at the infrastructure level) would be banking software where reads do not disproportionately outnumber the writes: you make payments and just forget about them for most of the time.