程序员除了会CRUD以外,还应该知道什么叫CQRS!

今天主要跟你们分享一下什么是 CQRS,以及在项目中如何去使用。前端

CRUD系统

咱们日常最熟悉的就是三层架构,一般都是经过数据访问层来修改或者查询数据,通常修改和查询使用的是相同的实体。而后经过业务层来处理业务逻辑,将处理结果封装成DTO对象返回给控制层,再经过前端渲染。反之亦然。数据库

这里基本上是围绕关系数据库构建而成的“建立、读取、更新、删除”系统(即CRUD系统),此类系统在一些业务逻辑简单的项目中可能没有什么问题,可是随着系统逻辑变得复杂,用户增多,这种设计就会出现一些性能问题。设计模式

咱们常常用到的解决方案就是对数据库进行读写分离。让主数据库处理事务性的增、删、改操做,让从数据库处理查询操做,而后主从数据库之间进行同步。可是这只是从DB角度处理了读写分离,从业务或者系统层面上来讲,读和写的逻辑仍然是存放在一块儿的,他们都是操做同一个实体对象。bash

这时候,CQRS 就该登场了。微信

CQRS系统

简单的说,CQRS(Command Query Responsibility Segration)就是一个系统,从架构上把 CRUD 系统拆分为两部分:命令(Command)处理和查询(Query)处理。其中命令处理包括增、删、改。架构

而后命令与查询两边能够用不一样的架构实现,以实现CQ两端(即Command Side,简称C端;Query Side,简称Q端)的分别优化。两边所涉及到的实体对象也能够不一样,从而继续演变成下面这样。app

固然了,CQRS 做为一个读写分离思想的架构,在数据存储方面,也没有作过多的约束。因此 CQRS能够有不一样层次的实现。异步

CQRS 实现方式

CQRS 能够有两种实现方式。ide

1)CQ 两端数据库共享,只是在上层代码上分离。这样作的好处是可让咱们的代码读写分离,更容易维护,并且不存在 CQ 两端的数据一致性问题,由于是共享一个数据库的。这种架构是很是实用的(也就是我上面画的那种)。性能

2)CQ 两端不只代码分离,数据库也分离,而后Q数数据由C端同步过来。同步方式有两种:同步或异步,若是须要 CQ 两端的强一致性,则须要用同步;若是能接受 CQ 两端数据的最终一致性,则可使用异步。C端能够采用Event Sourcing(简称ES)模式,全部C端的最新数据所有用 Domain Event 表达便可;而要查询显示用的数据,则从Q端的 ReadDB(关系型数据库)查询便可。

CQRS 的简单实现

说了这么多,该怎么实现呢?咱们以上面提到的第一种方式为例:代码层面实现分离,数据库共享。这种方式在企业里也很是实用。

首先有几个概念须要介绍一下,CQRS 模式中,首先须要有 Command,这个 Command 命令会对应一个实体和一个命令的执行类。那整个系统中确定有不少不一样的 Command,那么还须要一个 CommandBus 来作命令的分发处理。

可能你们以为比较抽象,我来写几行示例代码,一看就明白了。假设有个订单模块,我要新增一个订单信息。那么根据上文的分析,须要有个新增命令以及对应的订单实体(并不必定和数据库的订单实体彻底对应)。首先先建立一个命令接口(绑定命令对应的实体),接口内部有个该命令的处理方法。

public interface Command<T> {
    Object execute(T commandModel);
}
复制代码

OK,接下来咱们能够建立订单的新增命令了。

@Component
public class CreateOrderCommand implements Command<CreateOrderModel> {

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

到这里,咱们写好了具体的建立订单命令的逻辑,那么该命令须要放到 CommandBus 中去执行,因此咱们要写这个 CommandBus。

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

可能你们会看着有点晕,甚至有点绕,不要紧,我解释一下:这个 dispatch 方法就至关于分发执行,内部根据传入的具体 Command 以及对应的 model,去执行该 Command 实现的逻辑。

好了,那咱们在熟悉的 Controller 层该如何去调用呢?很简单,以下:

@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);
    }
}
复制代码

我还写了一个获取订单信息的接口,你们有没有发现,查询和插入是不一样的方式,插入走的是 CommandBus 分发到 CreateOrderCommand 去执行,而查询则是直接走 service 层去查。这就是 CQRS 模式。

固然了,当命令愈来愈多的时候,也能够将 CommandBus 抽象出接口,能够根据业务需求,实现多个不一样的 CommandBus 来分发命令。

除此以外,CQRS 还能够用在任务调度模块中,不一样的任务能够包含不一样的 Command,实际中运用是很是普遍的。

总结

CQRS 是一种思想很简单清晰的设计模式,他经过在业务上分离操做和查询来使得系统具备更好的可扩展性及性能,使得可以对系统的不一样部分进行扩展和优化。在 CQRS 中,全部的涉及到对 DB 的操做都是经过发送 Command,而后特定的 Command 触发对应事件来完成操做,也能够作成异步的,主要看业务上的需求了。

CQRS 虽然在思想上简单,可是实现上相对来讲复杂些,也涉及到 DDD 的一些概念了,固然了,这篇文章主要是介绍以及演示 CQRS 模式的基本实践,更多知识须要你们再深刻的去学习。

来源地:微信公众号

原文连接:mp.weixin.qq.com/s/-9TS3p7gS…

相关文章
相关标签/搜索