In modern systems, especially Internet software, it usually involves the concurrent access of a large number of users. Our system must support high performance and concurrent access in architecture. A high performance system usually consists of many aspects, including database high performance, Web server high performance, load balancing, caching, software architecture and so on. This article begins with an introduction to building high-performance systems from the perspective of software development architecture.
Traditional architecture performance issues
Let’s start by looking at the factors that adversely affect performance in the case of multi-user, high-concurrency access in the classic DDD architecture. Let’s start with a simple architecture diagram:
2. The domain model is used for both use cases and queries.
3. The current bounded context usually has only one WebApi project, in which different Action apis are used for different functions, including queries and useful cases.
As you can see from the above points, there are several reasons for performance bottlenecks:
1. Context query and use cases through a model to do, corresponding to the database, is to add, delete, change, search for the same database related tables, through blocking will cause performance problems; Although a good index can alleviate the problem, it is not a complete solution.
2. Domain models are usually designed for business needs, that is, for use cases. If it is used for query requirements, multiple service tables may be connected to complete the query, resulting in query performance problems.
3. Typically, after completing the domain logic, DDD uses application services to coordinate the domain logic with the repository to persist domain objects to the data store, and then returns user results through WebApi. If the domain is complex and the number of concurrent users is large, the process takes some time to feed back to the front end, resulting in poor user experience.
4. The current bounded context is a WebApi project, either a use case or a query; This also does not extend performance, such as use cases on some hosts and queries on other hosts.
To address these performance issues, the industry has developed an architectural style called CQRS (separation of command query responsibilities). Through the concept of CQRS, the system can effectively improve the support of large concurrency.
Commands refer to the action of changing the state of an object, which has side effects on the system; A query is an action that does not change the state of an object and has no side effects on the system. CQRS is not only used for large concurrent processing, but also for everyday development.
Commands mixed with queries:
private int Add(int a,int b)
{
int result = a + b;
return result;
}
Copy the code
As you can see from the code above, the change state is mixed with the query; We can modify the separation of command and query:
private int result;
private void Add(int a,int b)
{
result = a + b;
}
private int QueryResult()
{
return result;
}
Copy the code
Based on the above code ideas and classical DDD architecture problems, we introduced the CQRS architecture style to solve the performance problem.
CQRS architecture
Let’s take a look at the overall architecture of CQRS and then talk about how it addresses performance issues.
1. First of all, we can see that the command and query go through different WebApi services, so that the behavior of changing the system state can be well isolated from the behavior of query, and the micro service can be deployed to different servers, of course, can be further extended through NLB.
2. The WebApi on the command side does not directly process the completion of the call case, but releases the command message to the message bus when receiving the user command, and then immediately returns an operation information to the user. In this way, the user experience is very good and there is no need to wait for the completion of business logic and persistence.
3. The command processor WebApi listens to the message from the message queue and then processes it. The main content of processing is to complete the domain logic call and directly add the event data to the event store. It is important to note that this is not persisted to the business database. First complete the domain logic call, can get the use case finally correct domain object, and then store the event, store the state of the domain object, and is directly added.
The benefits of doing this include faster persistence and the ability to keep information about every change to a domain object for future use in history tracing, event tracing, and final consistency
4. The command handler sends the domain object to the message bus, and the event handler listens to the queue and eventually persists the domain object to which the message message relates to the business database.
5. In the query WebApi, you can directly query the business library. If the business library is not suitable for multi-table join query, you can separately make a leveled query library to provide services for the query. The contents of the query library can be updated successfully by the business library, then the message is published to another queue, and then the data is processed by the processor into the query library.
Micro service combat video, please pay attention to the wechat public number: MSSHCJ