Name says it...
CQRS (Command Query Responsibility Segregation) has been around for a while. Many software engineers understand it well, and many others don't. However, this term does not cover all forms of the conceptual communication if you want to be verbal and flexible enough with your choices. There's also an Event, which didn't make into this abbreviation, probably because it was not well understood at the time. However, Event is often discussed together with a Command (we'll talk about that below) and I insist that it needs to be discussed with the Query too. These three elements make up an entire breadth of communication dictionary between different components or nodes of the distributed or non-distributed systems. And since these three exist in the dictionary, regardless of whether you admit that or not, you need to learn how to distinguish between them, how to use them properly, and when to avoid one or the other.
Commands and Queries have been pals for a long time now. There's a basic difference between them - commands modify the data, and queries retrieve the data. They are put together in the context of the Responsibility Segregation because you should avoid mixing the two. This applies to all levels of architectures - in code you should not mix these two within a single method (CQS), and in distributed applications you should not mix a data retrieval with data modifications (CQRS).
There are some exceptions to this rule, but those are rather extensions and not exceptions, which means that these variations can live side by side. In general, mix is achieved by introducing a Facade over Commands and Queries, and it's called different things at different levels of architectures. In code, a Fluent Interface can expose a method that both modifies and returns the data, and this would be the Facade over Commands and Queries - this method calls existing commands and queries purely for the caller's convenience. In SOA, the Service Composability attribute allows defining yet another service operation that calls two other existing service operations - one being a command and another being a query. Again, these are extensions and not exceptions, so don't make it a habit to transform regular Commands and Queries into such mixes at all times. Resort to these only when necessary, otherwise always try to settle with the regular segregation of Commands and Queries.
Commands have made into another sort of architectures as well - distributed applications with messaging middleware for integration. It turned out that most (or I must say all) of the message brokers and service buses support Commands and Events. That's natural, because when you want to scale out, you are looking at things that you can afford to make 'not real time', and both Commands (often) and Events (always) have this attribute to them. You can place a Command or an Event on the message queue or topic, and let it be processed later on. The same cannot be said about the Query, as somebody must be waiting for the data on the screen after all, therefore Queries are not naturally supported by the messaging infrastructures.
Very important distinguishing factor between Commands and Events is their target of interest. Command is oriented towards the party that can process it, while Event is oriented towards the party that produced it. In other words, a specific Command, regardless of who asked for it, will always be processed in the same exact way; probably that's why it makes sense to think about the command handler as one thing, and not many variations. A specific event, regardless of how it's being handled and by whom, will always mean the same thing; probably that's why it makes sense to associate an event with just one particular 'maneuver' of the system or the application.
Both Commands and Events can also be expressed in code. Events are supported by all object oriented development languages so they won't require a special explanation; on the other hand, you execute a Command when you call a method directly instead of making it a handler of the event that you could fire instead.
With the term SQERS, I'm not intending to extend the CQRS in the context of a specific architecture or approach, I'm just trying to consider all three together. I think that these three elements are a bare minimum to understand the full communication capabilities within any application.
It's important that we distinguish and try to segregate these three elements from each other. Confusing or mixing the responsibilities can lead to unnecessary architectural complications and confusions, maintenance headaches, and legacy systems. This leads me to my next point...
While software engineers usually can say the Command from the Query, they don't hesitate to mix them at all. From CQRS standpoint, mixing the Command and Query interfaces makes it difficult to take different strategies for them (crux of CQRS). e.g. you might want to scale out the commands using a messaging middleware, while you might want to cache the queries for faster but stale reads. With mixed responsibilities, you can't separate these two problems from each other or do them in isolation. In the lower level code without the CQS pattern in mind, mixing the command and query responsibilities leads to violation of Single Responsibility principle and results in a poorly expressive code with side effects. e.g. when you invoke a method named 'GetPerson' on your repository, would you expect that another Person object will be changed because of this?
I have noticed that the differences between commands and events are harder to grasp for many software engineers, so they are often misused or unintentionally misplaced with each other. Specifically, people tend to fire off many commands in places where a single event would make much more sense. When you have this kind of confusion, you bloat the code with many commands dispatched, which is not an extensible architecture. Indeed, if one more independent post-event action needs to be added, you can't subscribe to an event but rather need to change the originating code to send one more command. Confusion can also happen in the other way around - when firing an event instead of dispatching a command. This often happens with the intention of loose coupling between the components that are supposed to be sequential instead. As a result, the solution will appear to be non-cohesive and will lack a higher lever structure between its components. This will complicate the system by making it harder to understand and harder to maintain, because too many dependencies will be implicit instead of being explicit and thus easy to follow and debug.
Real time operations should not be implemented as dispatchable commands or queries. I've seen this done, and it's very common. I discourage doing so because it will only create headaches where they are not supposed to be. Clear example is a so called Mediator pattern, also known as Mediator Pipeline, or Short Bus. This is an in-process framework that allows you dispatching an object (command, query, or event) in the code and then have it handled somewhere else by implementing respective generic interfaces. For events, it somewhat makes sense, but for commands and queries it makes me wonder. Just think about it: if you are dispatching a command but waiting for its answer until it's done (real time command), is it worth the trouble to abstract the command dispatching and passing through? Don't you already know that the command will be handled by one handler only? And so, can't you just inject the strongly typed class with the required operation right into the command dispatcher? Same is true for queries dispatched this way - they are real time, and they can only be fulfilled by the single query handler that retrieves the data and passes back. So, then why put an abstraction in between? These approaches are discouraged because they needlessly complicate code maintenance, understanding, unit-testing, and so on. One more con of such abstractions is that they will often use reflection to find the handler classes for commands and queries, so it's an additional performance hit too. In my opinion, such frameworks should be rewritten to support only dispatchable events, when being real time or not is not relevant anymore (event is naturally treated as not real time, so who cares if it's real time or not). When using these frameworks for real time commands and queries, you are complicating your life for the sake of using a framework; don't do that.
If you are curious about other nuances of software architecture, check out other articles or consider booking a technical training for your team.
The author of the above content is Tengiz Tutisani.
If you agree with the provided thoughts and want to learn more, here are a couple of suggestions: