24.CQRS:一种有界上下文的架构

1.单个模型处理两个有界上下文的面临的挑战

DDD强调使用聚合保持多个实体的数据一致性、处理并发、强制执行领域的不变条件,但是一个应用程序的报告需求(数据展示)可能和聚合的结构不一致(在存储库的末尾也提到过这个场景,https://www.jianshu.com/p/287555b75e38)此时面临的问题如下。

  1. 为了支持生成用于展示数据,聚合会遭到破坏。
  • 聚合需要公开内部状态,破坏了封装性。
  • 聚合增加与UI展示相关的属性,这些属性与领域不变条件不大(聚合是领域的抽象而不是依赖UI,聚合要围绕领域行为设计)。
  1. 读取往往比写入多很多,造成性能低下。

    UI需要多个聚合的极个别属性,但是聚合都是一次加载所有的内容的,会导致性能低下。

image.png

2. CQRS:复杂有界上下文的一种更好架构

CQRS(命令与查询职责分离),通过为读取和写入提供两个不同的模型分别处理业务请求。


image.png

2.1 命令端

image.png

命令端关注的是领域的规则,代表了满足领域任务的领域逻辑,但是不支持查询,返回结果是任务执行成功的确认。

一个命令就是一个业务任务,处于应用层内部,应该以业务语言编写(是捕获系统行为而非领域模型的术语和概念的语言),这里不是UL。

image.png

命令需要揭示业务意图,上面的例子表示明确表示用户需要兑换礼品券,应该是用动词,在领域事件里面用的是完成时。

image.png

命令端的领域模型是为了实现领域规则和逻辑,不需要有不必要的展现属性,聚合支持命令处理而非对现实环境建模,有利于专注行为和不变条件,保持聚合在较小的规模。

2.2 查询端

查询端的架构关注的是领域上的报告,查询的返回对象就是为了UI上展示的数据所定制的DTO,不需要领域模型,数据可以直接从数据库读取出来,利用ORM可以直接很简单的映射,如果一些属性在数据库没有预先写入,可以通过DTO里面的方法进行计算获取或者使用领域服务。

可以采用不同的数据结构进一步实现读取和写入分离,写入操作引发领域事件,由程序处理更新读取的数据结构的内容(这个主要是为了提高读性能)。

image.png

3.CQRS误解

3.1CQRS是最终一致性的

最终一致性是异步的更新读取模型的实现方式,比如上面说的通过领域事件更新,但是这个先决条件,它主要解决的还是提高读性能让读取端变的可扩展。CQRS目前主要解决读取和事务问题不一直带来的复杂性,可以使用相同的数据和事务更新读取模型结构。(如果大量使用了缓存系统会变成最终一致性)

3.2 需要事件溯源

事件溯源是同时构建读取和写入模型的方法,但不是必须的,主要目标确保审计追踪准确。

3.3 命令是异步的

看场景,高度协作的领域会有多个用户对相同数据进行变更,使用异步命令是合理的,确保命令依次处理,简单的场景没有必要。

3.4 需要采用消息

系统按照最终一致性设计或者异步任务时需要消息,否则会增加不必要的复杂性。

4.可以扩展的应用程序模式

4.1扩展读取端:最终一致的读取模型

image.png

提高读性能,消费更新数据的消息转换成读取需要的格式。

多个有界上下文的数据合并

image.png

消费领域事件

image.png

消费数据库的binlog

4.2 扩展写入端:使用异步命令

image.png

异步处理的任务要能够将状态反馈给客户端,否则会困惑。

4.3 同时进行扩展写入和读取

image.png

写入端通过消息异步处理数据更新写入模型,写入模型处理完成后发布消息更新读取模型。

你可能感兴趣的:(24.CQRS:一种有界上下文的架构)