什么是CQRS?

2,038 阅读3分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

CQRS

CQRS全称Command Query Responsibility Segregation,即命令查询职责分离,顾名思义,将命令和查询分离。

查询,就是查询数据(CRUD中的R),不会对数据产生变化,因此它是幂等的,不用担心对系统产生影响,因此也可以针对查询添加缓存操作提升查询性能。

那命令是啥呢?这里命令则是对数据产生变化的操作的总称(CRUD中的CUD)。

大多数软件系统中,查询频率要远大于命令操作,这是将查询与命令分离的根本原因。

CQRS架构图如下:

image.png

命令模式

命令模式在日常开发中比较常见,拿一个控制灯光的例子来说,可能我们会有如下的实现: image.png 命令模式会将执行动作和执行需要的信息封装成为一个Command,假如说要将灯光调整为25%、调整为50%,是否需要针对其每一个都创建一个命令类呢?这样会导致我们创建出无穷命令。

但是仔细想想,设置功率,仅仅只是数据变化,而具体怎么设置的业务逻辑并不会发生变化,可能你会想,可以通过创建一个命令类,通过execute的入参将变化的数据传递进来,但是如果这样,会破坏命令模式的初衷:将执行流程所需要的信息封装起来,使得其他调用地方执行流程时不进行感知。

或许你想也可以通过命令的构造器将变化的数据传入进去,但是这样并不优雅。它实际上是一种黑客行为,因为数据不是对象存在所需要的东西,它是它需要执行一些逻辑的东西,所以数据是方法的依赖,而不是对象的依赖。

命令总线(Command Bus)

核心目的是将不变变化分离开来。往往变化的是数据,而不变的则是业务逻辑。以上述例子来说,调整功率的具体代码业务逻辑不会变,仅仅是需要设置的功率在变化。因此可以分为两个类,其中之一为Command(存放数据DTO);另外一个为Handler(存放业务逻辑),其持有一个execute(CommandInterface $command): void方法用来触发业务逻辑的执行。此时上述的CommandInvoker也将演化,当接收到一个命令时,将查找该命令指定的命令处理器,然后调用处理器执行命令,那么这里我们就将它称为命令总线

使用命令总线,我们可以为选择异步执行命令,为系统带来更好的请求响应;而在一个集中的地方集中执行命令的一个好处就是可以在执行命令前后进行增强。命令总线一般会使用包装着它的装饰器来实现这一目标:

image.png

CQRS小结

通过CQRS模式将读模型和写模型分离,使得我们可以优化读性能和写性能之外,还可以让我们的代码更加清晰简洁,更加体现出领域,更易维护。

CQS,以及和CQRS的异同

newbedev.com/difference-…

danylomeister.blog/2020/06/25/…

参考

[1] DDD 中的那些模式 — CQRS
[2] 对CQRS的基础理解
[3] https://axoniq.io/resources/cqrs
[4] https://herbertograca.com/2017/10/19/from-cqs-to-cqrs/