As a system analysis methodology, the biggest problem of DDD is how to practice it in projects. In practice, there will be many problems. “Pattern” is a common method in the field of system architecture, which can help developers and architects to refer to the existing mature experience and solutions when encountering some tricky or unfamiliar problems, so as to solve the problems in their own projects gracefully.

Starting with this installment, I will introduce some of the common patterns in DDD, including their background, functions, advantages and disadvantages, and what to be aware of when using them. And the protagonist of this time is CQRS, Chinese name command query responsibility separation.

Why use it?

There is no doubt that “domain” occupies the core position in DDD. DDD realizes business logic and process through the interaction between domain objects, and separates business logic in a hierarchical way for separate maintenance, thus controlling the complexity of business itself.

But as a business system, “query” related functions are also indispensable. When implementing various query functions, you often find it difficult to use the domain model. Suppose the user needs a query function for order information, which presents a list of query results. The data in the list comes from a few fields in multiple domain objects such as “order”, “product”, “category”, “shipping address”, etc. Such scenarios are cumbersome if they are encapsulated by domain objects, and are not closely related to domain knowledge.

At this point, CQRS as a mode can well solve the above problems. Then what is CQRS? How do you do that?

What is CQRS?

CQRS – Command Query Responsibility Segregation is a mode of Segregation between Command and Query. So what is the word “query” or “command”?

CQRS divides the operations in the system into two categories: “commands” and “queries”. A command is a general term for operations that cause data to change, that is, operations that we often say add, update, delete, they are all commands. A query, on the other hand, does exactly what it says: it does nothing to the data, just looks it up under certain conditions.

The core idea of CQRS is to separate these two different types of operations and implement them in two separate “services.” The term “services” generally refers to two independently deployed applications. In some special cases, it can also be deployed on different interfaces within the same application.

The Command and Query data sources should also be independent of each other, meaning that updates are performed on one source and queries are performed on another. Now that the data sources are separated, how do you synchronize the data? Let’s move on.

Implement CQRS

Let’s take a look at the architecture diagram of CQRS:

As you can see from the figure, when the Command system completes the data update operation, it notifies the Query system through a “domain event”. The Query system updates its data source after receiving an event. All query operations are done through the interface exposed by the Query system.

From the architecture diagram, CQRS implementation does not seem difficult, many developers think of it as “add, delete, change” a system a database, “query” a system a database, similar to “read and write separation”, there is nothing special. But there are a number of issues and details to be worked out to actually use CQRS.

Problems with CQRS

The transaction

In fact, if you think about it carefully, you should quickly discover one of the biggest problems CQRS needs to face: transactions. In the original single process, single data source system, relying on the transaction characteristics of relational database can be a good guarantee of data integrity. But in CQRS all that has changed.

When the command end finishes the data update, it needs to notify the Query end system in the form of events, which has a certain time difference. If your business is very real-time for data integrity, then CQRS may not be suitable for you.

A second command-triggered event may require updating several data models on the Query side, which may also fail. Once the update fails, the data will remain in an inconsistent state for a long time, requiring external intervention. This is something to consider before using CQRS.

From a transactional perspective with CQRS, what you need to face is that the problem is ultimately one of consistency, so if your team doesn’t have much experience in this area, you need to learn ahead of time and gain some experience.

Infrastructure and technological capacity challenges

Another problem with CQRS is that there is no mature and easy-to-use framework. Axon may be one, but Axon itself is a heavyweight and highly dependent framework. Introducing Axon for CQRS is a bit of a waste of time, so most of the time you have to implement CQRS yourself.

A mature and reliable CQRS system has certain infrastructure requirements, for example, a reliable messaging middleware is indispensable in order to implement domain events. Otherwise, inconsistent data caused by frequent loss events will make the operation and maintenance personnel overwhelmed. The previously mentioned issues of distributed transactions and final consistency also require the support of specialized middleware or frameworks, which not only raises the requirements for infrastructure, but also for development, operation and maintenance.

The development process needs to add support for events, system design ideas also need to change. When you define a command, you need to design the event, the type of the event, and the data structure, so this aspect also puts new requirements on the development team.

Therefore, before starting to use CQRS, it is advisable to do a comprehensive assessment of your team’s infrastructure and development capabilities, identify shortcomings as soon as possible, and carry out targeted improvement and strengthening, to avoid some problems stuck in the development process.

Query model design

Although CQRS allows us to separate the domain model from the data model serving the query function, it means that we need to design another data model for the query function. It is generally simple to design according to the data required by the query function, that is, to design a data view for each query interface and update the associated data view when a domain event is received.

However, the problem brought by this simple approach is that when there are more and more query interfaces, it will be difficult to manage, still need to follow the idea of dividing BC in DDD to centralized management of queries belonging to a BC as a context of the whole query system, or simply do a separate microservice. So even with the introduction of CQRS, we still need to design the query interface using a domain-driven approach.

Relationship to Event Sourcing

Event Sourcing is an enterprise architecture pattern developed by Martin Fowler. In simple terms, it stores all the generated business activities in the system in the form of append-only, which is commonly known as the “running account”. Its advantage is that it can be “backtracked” because it records information about every data change, so it is very convenient when there are bugs or need to troubleshoot business data problems. However, its disadvantages are also obvious, that is, it needs to do a lot of calculations when it needs to query the latest status data, such as account balance data.

Many articles that discuss CQRS refer to Event Sourcing as two patterns that need to be used together. However, from the point of view of my actual use, the two modes are not necessarily related. The Command side only needs to care about whether the domain model is updated successfully or not, and uses domain objects such as Aggregate to ensure data integrity, while the Query side cares about updating the corresponding data model after receiving domain events. There is no mandatory requirement for the feature of “backtracking”. It is true that Event Sourcing can help us build a more stable and powerful CQRS system, but the complexity of Event Sourcing may be even greater than that of CQRS. Therefore, in the absence of special needs, CQRS and Event Sourcing need not be tied together.

Different types of data storage engines

This is not really a problem, but more of a challenge or strength. The separation of the domain model from the data model means that we can use data storage engines on the Query side that are closer to the Query requirements, such as NoSQL, ElasticSearch, etc.

It is common for the Command side to still use a traditional relational database, but to use a specialized data store for more specialized queries. For example, in some keyword-based full-text retrieval scenarios, if you still use a relational database, it is easy to encounter performance problems through SQL queries like. In this case, the data store can be replaced by a search engine such as ElasticSearch, which can extract keyword queries by reverse indexing, and the performance is significantly improved. In other scenarios requiring unstructured data Query, Json is a good storage format. Although relational databases of newer versions provide storage and Query in Json format, document databases like MongoDB are more simple and efficient, and the flexibility of Query side is more obvious.

summary

CQRS is a frequently mentioned pattern in DDD. Its purpose is to separate the domain model from the query function, so that some complex queries can get rid of the limitations of the domain model and present the query results in a simpler DTO form. At the same time, different data storage structures are separated, allowing developers to choose data storage engines more freely according to the function and requirements of the query.

Similarly, CQRS freedom and convenience brought in structure but also the inevitable introduced additional complexity and skill requirements, such as for distributed transactions, message middleware management, the design of the data model and so on, so the need for the team before introducing CQRS ability with a careful analysis of the existing structure, the short board necessary to ascend. CQRS is not recommended if the existing system is logically simple and CRUD only. However, if your business system is already very large, the business process is very complex, and the logic is very complex, then you can try to use CQRS to separate Command and Query, and the boundary between the domain model and the data model is clearer.

Welcome to follow my wechat account “And get more high-quality articles”