Going Down the CQS Rabbit Hole
A few weeks ago I attended a rather thought provoking presentation by Udi Dahan on the Command-Query Segregation Principle.
If you’re like me, then you might have confused this newer architectural principle with the other CQS (Command Query Separation), which is the classic OOP design principle that I wrote about in a recent post.
The story goes that Martin Fowler tried to warn Udi and his cohorts that this confusion would occur when they first coined this term, but they decided to stubbornly stick to the term because it described the the architectural approach so perfectly.
The driving force behind CQS is simply the belief that many of the scaling and complexity woes exhibited by the vast majority of systems today can be traced to the misguided effort to unify commands and queries into a single conceptual model.
The thought goes that once you truly break apart these divergent activities and try to solve each problem independently, then some innovative and sometimes even blasphemous ideas start to emerge.
If you’ve never heard Udi talk on the subject, then I recommend reading this fairly concise article on it.
Here is my interpretation of some of the more thought provoking points that I remember from the talk:
Query-Side
- This should be a read-only, de-normalized data-store with built-in latency that is separate from the command (transactional) data store.
- Data should be not only de-normalized, duplicated, and aggregated as needed, but also stored in the exact same format required by views. This will eliminate the need for DTO’s and the usual array of transformations that occur on the way from the data layer to the UI layer.
- Defining acceptable latency is key. Data should be organized according to acceptable staleness thresholds. Data may be duplicated many times to account for the fact that different pages usually have different tolerances for latency.
- Since data is flattened out completely there is no need for any relations, which also means that RDMS is probably not nearly as ideal as one of the NoSQL databases.
- Since these data stores only need read-only permissions, why do they need to be on a separate server behind the firewall? Udi points out that putting a document database on a web server that is in front of the firewall is actually more secure than using a caching solution. I can’t wait to have that conversation with our network guys and the PCI auditors…
Command-Side
- Commands should all be asynchronous. This means you fire off the command and immediately return with a message to the user saying that they’ll be notified when the transaction has been processed. It’s amazing how many things really don’t need to be synchronous when you stop to think about them. Udi makes the point that the world got along perfectly fine (and in some cases better) when it ran on paper, which is intrinsically an asynchronous process.
- Asynchronous is a better user experience. Is it really necessary to make 99.9% of the people wait an extra 10-15 seconds just because there is a slight chance that something may go wrong? Isn’t it a better user experience to handle the exceptional case manually or by having the user revisit the website to fix the problem?
- To be asynchronous, UI’s must capture intent instead of just being dumb data entry screens. In a classic ticketing system, a user has to select the exact seats he or she wants and then retry multple times because at least one of the seats was taken by another user since the screen first loaded. In the CQS model, the user would simply specify that they wanted five seats near each other that were all within a certain price range and then the system would later send out a notification with the results after sufficient requests were accepted and a packing algorithm was run to satisfy the highest number of requests possible.
- More onus is now placed upon the UI to ensure commands are valid and have a high probability of success. Once again, it is perfectly acceptable and cost efficient to handle the infrequent cases of failures by putting them in a manual intervention queue and even having a customer service person contact them.
- Asynchronous through a queuing mechanism like MSMQ is much more reliable and probably the only way to meet most SLA’s (Service Level Agreements).
- Each command should be totally independent (think separate VS solutions) so that they can be versioned separately. Udi refers to the benefits of reuse as largely a fallacy and the enemy of flexible versioning.
- Since domain models no longer have to support queries, there is no need for the web of relationships that usually just reflects the RDMS. Entities should only contain what is needed to support the command and can be duplicated across commands.
Here’s a diagram of this model from the paper I linked to at the top:
In conclusion, this is definitely one of the most thought provoking talks I’ve ever attended.
I’ve already lobbied my boss to send as many people as possible to attend Udi’s training, which covers this topic in much more depth.
I’ve also pushed the task of investigating projects like NServiceBus and MassTransit to the top of my list.
Most of these suggestions are too radical to immediately act upon, but I have to admit that I’m sold on the concepts.
Popularity: 1% [?]
Comments(1)


