In addition to CRUD, programmers should also know what CQRS is!

Today I mainly share with you what CQRS is and how to use it in the project.

CRUD system

The three-tier architecture that we are most familiar with is usually the data access layer to modify or query data. Generally, the same entity is used for modification and query. Then process the business logic through the business layer, encapsulate the processing result into a DTO object and return it to the control layer, and then render it through the front end. vice versa.

This is basically a "create, read, update, delete" system (ie, CRUD system) built around relational databases. Such systems may have no problem in some projects with simple business logic, but as the system logic changes If it is more complex and users increase, this design will have some performance problems.

The solution we often use is to separate read and write on the database. Let the master database handle transactional addition, deletion, and modification operations, and let the slave database handle query operations, and then synchronize between the master and slave databases. But this only deals with the separation of read and write from the perspective of DB. From the business or system level, the logic of read and write is still stored together, and they all operate on the same entity object.

This is where CQRS comes in.

CQRS system

Simply put, CQRS (Command Query Responsibility Segration) is a system that splits the CRUD system into two parts in terms of architecture: Command processing and Query processing. The command processing includes addition, deletion, and modification.

Then the two sides of the command and the query can be implemented with different architectures to realize the optimization of the two ends of the CQ (ie the Command Side, referred to as the C side; Query Side, referred to as the Q side). The entity objects involved on both sides can also be different, which continues to evolve into the following.

Of course, CQRS, as an architecture with read-write separation, does not impose too many constraints on data storage. So CQRS can have different levels of implementation.

How CQRS is implemented

CQRS can be implemented in two ways.

1) The databases at both ends of CQ are shared, but the upper-level code is separated. The advantage of this is that our code can be separated from reading and writing, which is easier to maintain, and there is no data consistency problem at both ends of the CQ because it shares a database. This architecture is very practical (that is, the kind I drew above).

2) Not only the code is separated at both ends of the CQ, but the database is also separated, and then the Q data is synchronized from the C end. There are two synchronization methods: synchronous or asynchronous. If strong consistency between the two ends of the CQ is required, synchronization is required; if the eventual consistency of the data at both ends of the CQ can be accepted, asynchronous can be used. The C-side can use the Event Sourcing (ES) mode, and all the latest data of the C-side can be expressed in Domain Event; and to query the data for display, it can be queried from the ReadDB (relational database) of the Q-side.

Simple implementation of CQRS

Having said all that, how to do it? Let's take the first method mentioned above as an example: separation at the code level and database sharing. This method is also very practical in the enterprise.

First of all, there are several concepts that need to be introduced. In the CQRS mode, there is a Command, which corresponds to an entity and a command execution class. There must be many different Commands in the whole system, so a CommandBus is needed to distribute the commands.

Maybe everyone thinks it is more abstract, I will write a few lines of sample code, and you will understand at a glance. Suppose there is an order module, and I want to add an order information. Then according to the above analysis, there needs to be a new command and a corresponding order entity (not necessarily exactly corresponding to the order entity in the database). First, create a command interface (the entity corresponding to the binding command), and there is a processing method for the command inside the interface.

public interface Command<T> {
    Object execute(T commandModel);
}

OK, then we can create a new command for the order.

@Component
public class CreateOrderCommand implements Command<CreateOrderModel> {

    @Override
    public Object execute(CreateOrderModel model) {
        // 具体的逻辑
    }
}

At this point, we have written the specific logic for creating an order command, then the command needs to be put into the CommandBus to execute, so we need to write this CommandBus.

@Component
public class CommandBus {
    public <T> Object dispatch(Command<T> cmd, T model) {
        return cmd.excute(model);
    }
}

You may look a little dizzy or even a little confused, it doesn't matter, let me explain: this dispatch method is equivalent to distribution execution, and the logic implemented by the Command is executed internally according to the specific Command passed in and the corresponding model.

Well, how do we call it in the familiar Controller layer? It's very simple, as follows:

@RestController
@RequestMapping(value = "/order")
public class OrderController {

    @Resource
    private GetOrderInfoService getOrderInfoService;
    @Resource
    private CreateOrderCommand createOrderCommand;
    @Resource
    private CommandBus commandBus;

    @PostMapping(value = "/getInfo")
    public Object getOrderInfo(GetOrderInfoModel model) {
        return getOrderInfoService.getOrderInfos(model);
    }

    @PostMapping(value = "/creat")
    public Object createOrderInfo(CreateOrderModel model) {
        return commandBus.dispatch(createOrderCommand, model);
    }
}

I also wrote an interface for obtaining order information. Have you noticed that query and insert are different ways. For insert, CommandBus is distributed to CreateOrderCommand for execution, while query is directly checked by the service layer. This is the CQRS pattern.

Of course, when there are more and more commands, CommandBus can also be abstracted from the interface, and multiple different CommandBus can be implemented to distribute commands according to business requirements.

In addition, CQRS can also be used in the task scheduling module. Different tasks can contain different Commands, which are widely used in practice.

Summarize

CQRS is a very simple and clear design pattern. It makes the system have better scalability and performance by separating operations and queries in business, so that different parts of the system can be expanded and optimized. In CQRS, all operations involving the DB are completed by sending Command, and then a specific Command triggers the corresponding event to complete the operation. It can also be made asynchronous, depending on the business requirements.

Although CQRS is simple in thought, its implementation is relatively complicated, and it also involves some concepts of DDD. Of course, this article mainly introduces and demonstrates the basic practice of CQRS mode. More knowledge requires you to go deeper. to learn.

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324155750&siteId=291194637