Clarified CQRS
Clarified CQRS
Clarified CQRS
After listening how the community has interpreted Command-Query Responsibility Segregation I think that the time has come for some clarification. Some have been tying it together to Event Sourcing. Most have been overlaying their previous layered architecture assumptions on it. Here I hope to identify CQRS itself, and describe in which places it can connect to other patterns.
Why CQRS
Before describing the details of CQRS we need to understand the two main driving forces behind it: collaboration and staleness. Collaboration refers to circumstances under which multiple actors will be using/modifying the same set of data whether or not the intention of the actors is actually to collaborate with each other. There are often rules which indicate which user can perform which kind of modification and modifications that may have been acceptable in one case may not be acceptable in others. Well give some examples shortly. Actors can be human like normal users, or automated like software. Staleness refers to the fact that in a collaborative environment, once data has been shown to a user, that same data may have been changed by another actor it is stale. Almost any system which makes use of a cache is serving stale data often for performance reasons. What this means is that we cannot entirely trust our users decisions, as they could have been made based on out-of-date information. Standard layered architectures dont explicitly deal with either of these issues. While putting everything in the same database may be one step in the direction of handling collaboration, staleness is usually exacerbated in those architectures by the use of caches as a performance-improving afterthought.
http://www.UdiDahan.com
Page 1
The boxes named AC are Autonomous Components. Well describe what makes them autonomous when discussing commands. But before we go into the complicated parts, lets start with queries:
Queries
If the data were going to be showing users is stale anyway, is it really necessary to go to the master database and get it from there? Why transform those 3rd normal form structures to domain objects if we just want data not any rule-preserving behaviors? Why transform those domain objects to DTOs to transfer them across a wire, and who said that wire has to be exactly there? Why transform those DTOs to view model objects? In short, it looks like were doing a heck of a lot of unnecessary work based on the assumption that reusing code that has already been written will be easier than just solving the problem at hand. Lets try a different approach: How about we create an additional data store whose data can be a bit out of sync with the master database I mean, the data were showing the user is stale anyway, so why not reflect in the data store itself. Well come up with an approach later to keep this data store more or less in sync. Now, what would be the correct structure for this data store? How about just like the view model? One table for each view. Then our client could simply SELECT * FROM MyViewTable (or possibly pass in an ID http://www.UdiDahan.com Page 2
in a where clause), and bind the result to the screen. That would be just as simple as can be. You could wrap that up with a thin facade if you feel the need, or with stored procedures, or using AutoMapper which can simply map from a data reader to your view model class. The thing is that the view model structures are already wire-friendly, so you dont need to transform them to anything else. You could even consider taking that data store and putting it in your web tier. Its just as secure as an inmemory cache in your web tier. Give your web servers SELECT only permissions on those tables and you should be fine.
Scaling Queries
Since your queries are now being performed off of a separate data store than your master database, and there is no assumption that the data thats being served is 100% up to date, you can easily add more instances of these stores without worrying that they dont contain the exact same data. The same mechanism that updates one instance can be used for many instances, as well see later. This gives you cheap horizontal scaling for your queries. Also, since your not doing nearly as much transformation, the latency per query goes down as well. Simple code is fast code.
Data modifications
Since our users are making decisions based on stale data, we need to be more discerning about which things we let through. Heres a scenario explaining why: Lets say we have a customer service representative who is one the phone with a customer. This user is looking at the customers details on the screen and wants to make them a preferred customer, as well as modifying their address, changing their title from Ms to Mrs, changing their last name, and indicating that theyre now married. What the user doesnt know is that after opening the screen, an event arrived from the billing department indicating that this same customer doesnt pay their bills theyre delinquent. At this point, our user submits their changes. Should we accept their changes? http://www.UdiDahan.com Page 3
Well, we should accept some of them, but not the change to preferred, since the customer is delinquent. But writing those kinds of checks is a pain we need to do a diff on the data, infer what the changes mean, which ones are related to each other (name change, title change) and which are separate, identify which data to check against not just compared to the data the user retrieved, but compared to the current state in the database, and then reject or accept. Unfortunately for our users, we tend to reject the whole thing if any part of it is off. At that point, our users have to refresh their screen to get the up-to-date data, and retype in all the previous changes, hoping that this time we wont yell at them because of an optimistic concurrency conflict. As we get larger entities with more fields on them, we also get more actors working with those same entities, and the higher the likelihood that something will touch some attribute of them at any given time, increasing the number of concurrency conflicts. If only there was some way for our users to provide us with the right level of granularity and intent when modifying data. Thats what commands are all about.
Commands
A core element of CQRS is rethinking the design of the user interface to enable us to capture our users intent such that making a customer preferred is a different unit of work for the user than indicating that the customer has moved or that theyve gotten married. Using an Excel-like UI for data changes doesnt capture intent, as we saw above. We could even consider allowing our users to submit a new command even before theyve received confirmation on the previous one. We could have a little widget on the side showing the user their pending commands, checking them off asynchronously as we receive confirmation from the server, or marking them with an X if they fail. The user could then double-click that failed task to find information about what happened. Note that the client sends commands to the server it doesnt publish them. Publishing is reserved for events which state a fact that something has happened, and that the publisher has no concern about what receivers of that event do with it.
As such, validation can be performed on the client, checking that all fields required for that command are there, number and date ranges are OK, that kind of thing. The server would still validate all commands that arrive, not trusting clients to do the validation.
http://www.UdiDahan.com
Page 5
Autonomous Components
While in the picture above we see all commands going to the same AC, we could logically have each command processed by a different AC, each with its own queue. That would give us visibility into which queue was the longest, letting us see very easily which part of the system was the bottleneck. While this is interesting for developers, it is critical for system administrators. Since commands wait in queues, we can now add more processing nodes behind those queues (using the distributor with NServiceBus) so that were only scaling the part of the system thats slow. No need to waste servers on any other requests.
Service Layers
Our command processing objects in the various autonomous components actually make up our service layer. The reason you dont see this layer explicitly represented in CQRS is that it isnt really there, at least not as an identifiable logical collection of related objects heres why: In the layered architecture (AKA 3-Tier) approach, there is no statement about dependencies between objects within a layer, or rather it is implied to be allowed. However, when taking a command-oriented view on the service layer, what we see are objects handling different types of commands. Each http://www.UdiDahan.com Page 6
command is independent of the other, so why should we allow the objects which handle them to depend on each other? Dependencies are things which should be avoided, unless there is good reason for them. Keeping the command handling objects independent of each other will allow us to more easily version our system, one command at a time, not needing even to bring down the entire system, given that the new version is backwards compatible with the previous one. Therefore, keep each command handler in its own VS project, or possibly even in its own solution, thus guiding developers away from introducing dependencies in the name of reuse (its a fallacy). If you do decide as a deployment concern, that you want to put them all in the same process feeding off of the same queue, you can ILMerge those assemblies and host them together, but understand that you will be undoing much of the benefits of your autonomous components.
http://www.UdiDahan.com
Page 7
Let me reiterate How you process the commands is an implementation detail of CQRS.
http://www.UdiDahan.com
Page 8
Bounded Contexts
While CQRS touches on many pieces of software architecture, it is still not at the top of the food chain. CQRS if used is employed within a bounded context (DDD) or a business component (SOA) a cohesive piece of the problem domain. The events published by one BC are subscribed to by other BCs, each updating their query and command data stores as needed. UIs from the CQRS found in each BC can be mashed up in a single application, providing users a single composite view on all parts of the problem domain. Composite UI frameworks are very useful for these cases.
http://www.UdiDahan.com
Page 9
Summary
CQRS is about coming up with an appropriate architecture for multi-user collaborative applications. It explicitly takes into account factors like data staleness and volatility and exploits those characteristics for creating simpler and more scalable constructs. One cannot truly enjoy the benefits of CQRS without considering the user-interface, making it capture user intent explicitly. When taking into account client-side validation, command structures may be somewhat adjusted. Thinking through the order in which commands and events are processed can lead to notification patterns which make returning errors unnecessary. While the result of applying CQRS to a given project is a more maintainable and performant code base, this simplicity and scalability require understanding the detailed business requirements and are not the result of any technical best practice. If anything, we can see a plethora of approaches to apparently similar problems being used together data readers and domain models, one-way messaging and synchronous calls. Although this article is over 3000 words, I know that it doesnt go into enough depth on the topic - it takes about 3 days out of the 5 of my Advanced Distributed Systems Design course to cover everything in enough depth. Still, I hope it has given you the understanding of why CQRS is the way it is and possibly opened your eyes to other ways of looking at the design of distributed systems.
http://www.UdiDahan.com
Page 10