系统设计 — CQRS应用架构模式解析

1、CQRS架构

在这里插入图片描述
上面图中包含有很多的概念,先列举一下所涉及的概念:

  • Command Bus(命令总线):在 Command Handler 之前,可以看作是 Command 发布者。
  • Command Handler(命令处理器):处理来自 Command Bus 分发的请求,可以看作是 Command 订阅者、处理者。
  • Event Bus(事件总线):一般在 Command Handler 完成之后,可以看作是 Event 发布者。
  • Event Handler(事件处理器):处理来自 Event Bus 分发的请求,可以看作是 Event 订阅者、处理者。
  • Event Store(事件存储):对应概念 Event Sourcing(事件溯源),可以用于事件回放处理,还原指定对象状态。

首先抽离两个重要概念:Command(命令)和 Event(事件) Command 是一种命令的语气,它的效果就是对某种对象状态的修改,Command Bus 收集上层Command 命令,并根据具体命令分发给具体的 Command Handler 进行处理,这时候就会产生一些领域操作,并对相应的领域对象进行修改,Command Handler 只是修改操作,并不会涉及到修改之后的操作(比如保存、事件发布等),Command Handler 完成之后并不表示这个 Command 命令就此结束,它需要把接下来的操作交给 Event Bus(完成之后的操作),并分发给相应的 Event Handler 订阅者进行处理,一般是数据保存、事件存储等。关于 Event Handler 保存领域状态操作一般会采用异步的方式,也就是说领域状态的保存操作不会延时领域中的业务操作,数据的一致性使用 Unit of Work,具体的领域状态保存用 Repository 实现。

梳理 Command 整个流程,你会发现一个关键词:状态(Status),Command Bus 接收来自 上层的 ,Command 分发给相应的 Command Handler 进行处理,在处理过程中,就会对领域对象进行修改操作,但它不会保存修改之后的状态信息,而是交给 Event Handler 进行保存状态信息。

和 Command 相比,Query 的处理流程就简单很多了,Query Service 接收了查询请求,这个查询处理可以用各种方式实现,你可以使用 ORM,也可以直接写 SQL 代码,反正是:怎么能提高性能,就怎么来!返回的结果类型一般是 DTO(数据传输对象),根据 UI 进行设计,可以减少不必要的数据传输。

2、CQRS概念

命令查询职责隔离。将持久化数据模型和使用数据的模块分为两部分:命令端和查询端。命令端模块和数据模型实现CUD操作,查询端模块和数据模型实现查询。查询端通过订阅命令端发布的事件,使其数据模型与命令端数据模型保持同步。

  • 命令(Command):不返回任何结果(void),但会改变对象的状态。
  • 查询(Query):返回结果,但是不会改变对象的状态,对系统没有副作用。

按照这种架构,业务逻辑层的两个组件将会相互独立地运行。因此,读取模型(Read Model)将会处理用户的查询——在处理能力方面的要求会更少一些,而写入模型(Write Model)将会经历一个很长的处理路径,包括校验、队列、消息以及用户命令要执行的业务规则处理。

CQRS 模式强调在实现应用的时候,将解决业务问题放在第一位。它关注的中心点在于业务领域及其发挥作用的上下文之间的紧密协作。它会聚焦于业务而不是技术问题,并且要找出特定领域中所有细微的内容,这之所以能够实现要归功于采用了一种通用语言(Ubiquitous language)。这门语言能够让所有的团队成员(业务和技术)共享工作成果,他们会定义通用的业务对象来描述解决方案的领域模型,还会定义这种模型中的特定上下文,在这一点上要取得共识。随后,这些业务对象会被代码本身来使用。

因为基于 CQRS 的架构允许将逻辑分为两种不同类型的任务,所以它创造了一个有利条件,能够使用这些明确的对象来编写代码并且能够强化以业务为中心的 DDD 方式的影响力。技术团队更加关注于业务规则和处理流程,而不是基础设施或者不同类型的架构所带来的代码冲突。

CQRS的好处

  • 在微服务架构中高效地实现查询
  • 高效地实现多个不同的查询类型
  • 在基于事件溯源技术的应用中实现了查询
  • 更进一步地实现问题隔离

CQRS的弊端

  • 更加复杂的架构
  • 处理数据复制导致的延迟
  • 一种解决方案是采用命令端和查询端API为客户端提供版本信息,使其能够判断查询端是否过时。
  • 应尽可能使用API组合

适宜CQRS的场景

  • 当在业务逻辑层有很多操作需要相同的实体或者对象进行操作的时候。CQRS使得我们可以对读和写定义不同的实体和方法,从而可以减少或者避免对某一方面的更改造成冲突
  • 对于一些基于任务的用户交互系统,通常这类系统会引导用户通过一系列复杂的步骤和操作,通常会需要一些复杂的领域模型,并且整个团队已经熟悉领域驱动设计技术。写模型有很多和业务逻辑相关的命令操作的堆,输入验证,业务逻辑验证来保证数据的一致性。读模型没有业务逻辑以及验证堆,仅仅是返回DTO对象为视图模型提供数据。读模型最终和写模型相一致
  • 适用于一些需要对查询性能和写入性能分开进行优化的系统,尤其是读/写比非常高的系统,横向扩展是必须的。比如,在很多系统中读操作的请求时远大于写操作。为适应这种场景,可以考虑将写模型抽离出来单独扩展,而将写模型运行在一个或者少数几个实例上。少量的写模型实例能够减少合并冲突发生的情况
  • 适用于一些团队中,一些有经验的开发者可以关注复杂的领域模型,这些用到写操作,而另一些经验较少的开发者可以关注用户界面上的读模型
  • 对于系统在将来会随着时间不段演化,有可能会包含不同版本的模型,或者业务规则经常变化的系统
    需要和其他系统整合,特别是需要和事件溯源Event Sourcing进行整合的系统,这样子系统的临时异常不会影响整个系统的其他部分。

不适宜CQRS的场景

  • 领域模型或者业务逻辑比较简单,这种情况下使用CQRS会把系统搞复杂。
  • 对于简单的,CRUD模式的用户界面以及与之相关的数据访问操作已经足够的话,没必要使用CQRS,这些都是一个简单的对数据进行增删改查。
  • 不适合在整个系统中到处使用该模式。在整个数据管理场景中的特定模块中CQRS可能比较有用。但是在有些地方使用CQRS会增加系统不必要的复杂性。

3、总结

CQRS是一种思想很简单清晰的设计模式,他通过在业务上分离操作和查询来使得系统具有更好的可扩展性及性能,使得能够对系统的不同部分进行扩展和优化。在CQRS中,所有的涉及到对DB的操作都是通过发送Command,然后特定的Command触发对应事件来完成操作,这个过程是异步的,并且所有涉及到对系统的变更行为都包含在具体的事件中,结合Eventing Source模式,可以记录下所有的事件,而不是以往的某一点的数据信息,这些信息可以作为系统的操作日志,可以来对系统进行回退或者重放。CQRS 模式在实现上有些复杂,很多地方比如AggregationRoot、Domain Object都涉及到DDD中的相关概念。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RachelHwang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值