We start with a warning – the authors do not recommend CQRS as a "global" architecture. A complex software system cannot be set up using a single architecture model throughout. Architectural decisions should be explicit and made separately for each subsection. This is essential for ensuring optimal implementation of differing non-functional requirements. Nonetheless, CQRS is a useful approach for a range of independent problems. Below, we present types of system components for which using CQRS presents a significant benefit.
Components in which data is read significantly more frequently than it is modified are the most obvious beneficiaries of the separation into read and domain models. For all forms of data queries, such as an application's screen content, printouts in reporting or interfaces to external systems, the required data is always present exactly as required by the particular data query. The result is that performance when querying data is always as fast as it can be. Changes are processed independently of the read model. The data is not modified until afterwards, minimising locking.
Components of a system's core domain frequently provide added value by means of complex behaviour, the specification for which, for a variety of reasons, changes over time. These reasons may include dealing with new customer requirements, market changes or new legislation, or may come from sales and marketing departments expecting added value from extensions to the system. For volatile and evolving systems in particular, CQRS helps retain maintenance and development flexibility. Freed from the burden of provisioning data, constructing components using CQRS enables lightweight implementation of component behaviour. Starting from clearly recorded user intentions in command objects, the structure of the source code is based on expertly specified requirements. In combination with event sourcing, these requirements can be optimally tested using acceptance tests. By formulating the requirements for a test and the expected results as events, requirements are transferred directly into code. Conversely, expert users can read and understand test logs immediately (figure 4).
The core use case for CQRS is highly collaborative systems with frequent concurrent access by multiple users. In such cases, CQRS does away with conventional locking and contention. By recording the user's intention in the commands, it is possible to define for each command individually, whether the triggered behaviour collides with or is compatible with other commands or the current system state. Running the commands "add product photo" and "increase product price by €20" in an ERP system, for example, does not cause a collision, despite the fact that they affect the same product.
In a CRUD application in which both users are only able to perform a single "SaveChangeToProduct" action, the decision is simply whether to use optimistic or pessimistic locking. The result is that one of the two edits would either be blocked in advance ("That product is currently being edited by another user.") or would fail ("An edit conflict has occurred").
Even where two commands genuinely conflict, such as "add product to shopping cart" and "set product status to not available", it is possible to define how the software should handle the conflict individually. A product which is not available should no longer be able to be ordered, irrespective of which command reaches the domain a moment or two earlier. If a collision affects a product with an order value which exceeds a certain sum, the system could resolve the collision in favour of the order, since the benefit to the company is such that it is worth the trouble of subsequently resolving the problem manually. For the company, the cost of manually reordering or providing a free upgrade to a more expensive product is smaller than the loss resulting from an otherwise lost sale.
A common requirement for software is detailed traceability of all processes within the system. CQRS with event sourcing just happens to meet this requirement out of the box. The event history is a guaranteed complete log, since the state of the system is derived from it. In addition, it is useful for analysing errors and unexpected behaviours. Instead of the usual support procedures – analysing the state database from the backup, talking to users – the course of events can simply be read from the history. A past system state can also be reconstructed at any time for forensic purposes.
Risks and side effects
The use of CQRS results in a much simplified system architecture. For many developers, however, this initially "feels" extremely complex. This is not so much a result of an apparently steep learning curve – there is an interesting podcast project which shows that this is actually fairly flat. The difficulty is often caused more by having to unlearn the use of complicated enterprise frameworks. Looked at objectively, this definitely represents a risk, as major changes to a development department's tool chest can initially result in a serious collapse in productivity. There is also a risk of miscalculations as a result of lack of experience with CQRS as a tool. Consequently, when introducing any new method, it is a good idea to start by practising on a project which is relatively non-business critical.
A more concrete problem is the lack of comprehensive tool support for the components of a CQRS architecture. It is for precisely this reason that the authors have fallen back on SQL Server implementations on their most recent projects. The range of tools available is now much improved, however. A commercial, open-source event store with professional support – something which is often essential for the core data storage components – has been available since autumn 2012.
There is another side effect which needs mentioning. Particularly in simple systems, the strong focus on software behaviour results in an increase in analysis and design work compared to pure data structure implementations, in which behaviour can simply be omitted and rule-compliant editing of data delegated to the user. Because CQRS focuses on recording the user's intention and processing business logic based on this intention, it is hard to get around a clean analysis of the problem domain. Whether that is an advantage or disadvantage is for the reader to decide.