What is CQRS?
CQRS Is an alternative architecture and pattern for Domain Driven Design (But you don’t basically need DDD to apply CQRS). A method should either change the state of an object, or return a result, but not both. In other words, asking the question should not change the answer. CQRS is a pattern that strictly segregates the responsibility of handling command input into an autonomous system from the responsibility of handling side-effect-free query (read) access on the same system. In simple terms, your service implementation for the query should be separated from command. Just like REST terminology, get and query requests is something different from the push, delete and update. So you can optimize the read side of the system separately from the write side. The write side is known as the domain and The domain contains all the behavior. You can bypass the domain model and get DTOs directly from the data storage using a read layer. When an application is requesting data, this could be done by a single call to the read layer which returns a single DTO containing all the needed data.
CQRS and DDD
Domain-Driven Design is extremely important for successfully delivering and maintaining CQRS-based systems. DDD works as a strategic approach that allows breaking complex problem domains into separate blocks (called bounded contexts) while accounting for things like different mental models, organizational politics, domain linguistics etc. While doing that, DDD establishes a mental model of bounded context, that is both understandable by business people and can easily be implemented in the code. The reasons for identifying context boundaries for your domain models are not necessarily the same reasons for choosing the portions of the system that should use the CQRS pattern. In DDD, a bounded context defines the context for a model and the scope of a ubiquitous language. You should implement the CQRS pattern to gain certain benefits for your application such as scalability, simplicity, and maintainability. Because of these differences, it may make sense to think about applying the CQRS pattern to business components rather than bounded contexts.
Read and write sides
Objects or the read side contain only query methods, and the objects on the "write" side contain only command methods. you can scale out the read side to support the larger number of reading operations independently of the write side. Typically, commands involve complex business logic to ensure that the system writes correct and consistent data to the data store. A single conceptual model that tries to encapsulate both read and write operations may do neither well. Segregating the two sides ultimately results in simpler, more maintainable, and more flexible models. With CQRS you can achieve great read and write performance. By separating read and write operations, each can be optimized. With CQRS you can split development tasks between different teams with defined interfaces (read and write). A direct connection to the data source makes queries very easy to buy maintained and optimized. So It makes sense to denormalize data. The reason for this is that data is normally queried many times more than the domain behavior is executed. This denormalization could increase the performance of the application.
Command: change the state of an object or entire system. A command changes the state of an object but does not return any data.
Query: return results and do not change the state of an object. queries will declare the return type. The reading layer can be directly connected to the database and query via a stored procedure or an ORM. A query returns data and does not alter the state of the object. In the picture below (described in Microsoft articles which I've referenced at the end), a user requests and make the change in the state of the system and receives via query side.
Events in CQRS
1-Internal Event: The domain event is something that has happened in the system in the past. The event which is handled inside the aggregate root of DDD is called an internal event. The event handler should not be doing any logic instead of setting the state. every state change of an Aggregate Root is triggered by an event and the internal event handler of the Aggregate Root has no other role than setting the correct state. If you find that some logic in your system is generating the wrong events, you must generate a new compensating event correcting the results of the previous bug events.
2-External event: External events are usually used for bringing the reporting database in sync with the current state of the domain. This is done by publishing the internal event outside the domain. When an event is published then the appropriate Event Handler handles the event. External events can be published to multiple event handlers.
The Event handlers perform the following tasks:
- It receives an Event instance from the messaging infrastructure (Event Bus).
- It locates the process manager instance that is the target of the Event.
- It invokes the appropriate method of the process manager instance passing in any parameters from the event.
- It persists the new state of the process manager to storage.
- But who can publish the events? Usually, the domain repository is responsible for publishing external events.
- The only entry point for the CQRS example is the Command Bus into which commands are sent.
What kinds of problems does CQRS solve?
Scalability: In many enterprise systems, the number of reads vastly exceeds the number of writes, so your scalability requirements will be different for each side. By separating the read side and the write side into separate models within the bounded context, you now have the ability to scale each one of them independently. Scalability should not be the only reason why you choose to implement the CQRS pattern in a specific bounded context.
Reduced complexity: In complex areas of your domain, designing and implementing objects that are responsible for both reading and writing data can exacerbate the complexity. In many cases, the complex business logic is only applied when the system is handling updates and transactional operations; in comparison, read logic is often much simpler. When the business logic and read logic are mixed together in the same model, it becomes much harder to deal with difficult issues such as multiple users, shared data, performance, transactions, consistency, and stale data. Separating the read logic and business logic into separate models makes it easier to separate out and address these complex issues. However, in many cases, it may require some effort to disentangle and understand the existing model in the domain. Separation of concerns is the key motivation about Command Query Separation Principle. Like many patterns, you can view the CQRS pattern as a mechanism for shifting some of the complexity inherent in your domain into something that is well known, well understood, and that offers a standard approach to solving certain categories of problems. Another potential benefit of simplifying the bounded context by separating out the read logic and the business logic is that it can make testing easier.
"The really valuable idea in this principle is that it's extremely handy if you can clearly separate methods that change state from those that don't. This is because you can use queries in many situations with much more confidence, introducing them anywhere, changing their order. You have to be more careful with modifiers."
—Martin Fowler, CommandQuerySeparation
When Should I choose CQRS?
Because of separating the read and write side, it is useful for Task-based and inductive User interfaces applications. Using the CQRS pattern could be valuable when your system has a collaborative and large amount of data, complex and ever changing business rules. CQRS forces you to not mix domain logic and infrastructural operations. CQRS is not useful in any case, like other patterns. CQRS could be implemented on a bounded context and also could be used in the whole of the system. A complex domain may be easier to tackle by using CQRS. Each domain has different characteristics, usually, there's enough overlap between the command and query sides that sharing a model is easier. CQRS allows you to separate the load from reads and writes allowing you to scale each independently. The other main benefit is in handling high-performance applications. If your application sees a big disparity between reads and writes this is very handy. Even without that, you can apply different optimization strategies to the two sides. An example of this is using different database access techniques for reading and update. In general, applying the CQRS pattern may provide the most value in those bounded contexts that are collaborative, complex, include ever-changing business rules, and deliver a significant competitive advantage to the business. Analyzing the business requirements, building a useful model, expressing it in code, and implementing it using the CQRS pattern for such a bounded context all take time and cost money. You should expect this investment to pay dividends in the medium to long-term. It is probably not worth making this investment if you don't expect to see returns such as increased adaptability and flexibility in the system, or reduced maintenance costs. You can apply CQRS when you need to migrate your architecture towards an event-driven architecture - CQRS as a pattern is a good stepping stone. it's hard to get face-to-face communication, so you want to decouple the read models from the write-side of things (sagas, domain, CRUD) and you want to formalize the data merge concepts of your domain you want to scale horizontally and not vertically.
When not to Use CQRS?
You should not apply the CQRS pattern to the top level of your system. You should clearly identify the different portions of your system that you can design and implement largely independently of each other, and then only apply the CQRS pattern to those portions where there are clear business benefits in doing so. If you are not developing a highly collaborative system where you don't have multiple writers to the same logical set of data you shouldn't use CQRS. Simple, static, non-core bounded contexts are less likely to warrant the up-front investment in detailed analysis, modeling, and complex implementation. Again, non-collaborative bounded contexts are less likely to see benefits from applying the CQRS pattern. In most systems, the majority of bounded contexts will probably not benefit from using the CQRS pattern. You should only use the pattern when you can identify clear business benefits from doing so. CQRS naturally fits with some other architectural patterns. As we move away from a single representation that we interact with via CRUD, we can easily move to a task-based UI. Having separate models raises questions about how hard to keep those models consistent, which raises the likelihood of using eventual consistency. CQRS is suited to complex domains, the kind that also benefits from Domain-Driven Design.
"It's important to note though, that these are things you can do, not necessarily things you should do. Separating the read and write models can be quite costly."
—Greg Young, CQRS and CAP Theorem.
I covered some basic concepts about CQRS pattern if you take a look at these pictures you can find out how it works and what's CQRS for.
The CQRS pattern is an enabler for building individual portions (bounded contexts) in your system. Identifying where to use the CQRS pattern requires you to analyze the trade-offs between the initial cost and overhead of implementing the pattern and the future business benefits. Useful heuristics for identifying where you might apply the CQRS pattern are to look for components that are complex, involve fluid business rules, deliver competitive advantage to the business, and are collaborative. I collected some information about CQRS pattern in this article, as I'm learning this pattern I'm sure that it might be some kinds of technical mistakes in it, so I would be thankful if you tell me about that.