mcpField
A
arch-cqrs

arch-cqrs

github.com/yonatankarp开发139 次安装23 周前更新
代码仓库

关于 arch-cqrs

arch-cqrs 是介绍命令查询职责分离的架构技能,说明如何拆分写入模型与查询模型,并帮助团队评估适用场景、投影方式和最终一致性。

主要功能

  1. 01拆分命令模型与查询模型
  2. 02通过事件更新读取模型
  3. 03比较共享或独立存储
  4. 04评估最终一致性
  5. 05识别 CQRS 反模式

适用场景

  1. 01应对读写负载不均
  2. 02提供仪表盘、搜索和详情视图
  3. 03将事务写入与查询存储配合

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yonatankarp/software-design-skills --skill arch-cqrs

安装后在 Claude Code 中输入/arch-cqrs确认已加载。

详细介绍

一句话总结

将用于命令(改变状态的操作)的模型与用于查询(只读视图)的模型分开。两者可以独立演进、扩展和持久化。

适用场景

  • 读负载与写负载严重不对称——每次写入对应许多次查询,或反之。
  • 不同的读取场景需要同一份数据的不同形态(仪表板、搜索、详情页)——采用单一规范读取模型会让步求全。
  • 自然的写入模型(例如带有丰富不变量的聚合)不便于查询视图。
  • 写入端受益于事件溯源,但读取端需要快速的 SQL 查询——CQRS 可以充当桥梁。

不适用场景

  • 读写负载大致均衡,且使用相同的数据形态——CQRS 只会增加开销。
  • 团队不熟悉最终一致性——CQRS 会有意引入这种机制。
  • 系统规模较小,使用常规仓储模式即可满足需求——属于过度设计。
  • 用“CQRS”来为同一份数据保留两张数据库表辩护,却没有实际需求驱动——这只是数据重复。

核心内容

两个模型,一个底层事实。

  • 写入模型——接收命令(PlaceOrder、CancelShipment)。进行验证、强制执行不变量并持久化。通常是一个聚合(ddd-aggregate)。针对一致性优化,而非读取。
  • 读取模型——接收查询。返回针对特定使用方定制的 DTO。可以采用反规范化、预先联接、缓存或为搜索建立索引。不强制执行不变量——它是一种投影。

同步方式。 写入会生成事件(通常是领域事件;参见 ddd-domain-event)。读取模型是投影——处理程序使用事件并更新物化视图。读取端总是略微落后于写入端。这就是最终一致性;应有意识地接受它。

存储方式。

  • 同一数据库,两个模型。 最简单的方式。写入模型和读取模型使用同一数据库中的不同表(甚至不同架构)。读取模型通过触发器、定时任务或进程内投影来填充。
  • 不同数据库。 写入事务型存储(聚合使用 Postgres);投影到查询存储(搜索使用 Elasticsearch;热点查询使用 Redis;仪表板使用单独的 Postgres 架构)。会带来实际的运维复杂性,只有确有读取需求时才值得采用。

CQRS 并不要求事件溯源。 两者经常配合使用,但彼此独立。你可以在普通的 CRUD 写入模型上采用 CQRS,并让它在提交时生成事件——若事件本身是事实来源,请参见 arch-event-sourcing。

关于最终一致性的讨论。 读取模型存在延迟。如果领域专家表示“用户期望立即在仪表板上看到自己的更改”,你可以选择缩短投影延迟,或者接受在 UI 中提示“正在处理您的更改”。不要假装这种延迟不存在。

决策启发

  • 只有在读写不对称性可衡量,或读写之间的形态存在实质差异时才使用。不要因为“CQRS 听起来很先进”就采用。
  • 默认先采用单数据库 CQRS(在一个 Postgres 中使用两个模型)。只有在读取存储的特性(搜索、地理空间、内存存储)与写入存储确实不同时,才使用不同数据库。
  • 只有在存在下游事件使用方时才采用事件驱动;否则更简单的投影就足够了。
  • 读取模型是投影,不是事实来源。如果你发现自己先读取、再通过读取模型写回,就违背了 CQRS。

反模式

  • 使用单一模型实现“CQRS”。 有两个名为 commands/ 和 queries/ 的文件夹,但它们读写的是同一张表,没有任何分离。这只是做戏。
  • 读模型变成直写模型。 代码绕过写模型,直接写入读取存储。两个模型会在无声无息中逐渐偏离。
  • 向 UI 隐瞒最终一致性。 用户进行了更改,UI 却显示过时数据,用户会以为更改失败了。应告知用户延迟情况。
  • 读模型迁移过重。 每次架构变更都需要完整重新投影,过程缓慢且风险较高。对读模型进行版本管理,并并行运行迁移。
  • 因写读一致而采用(没有不对称性)。 只有纯粹的额外开销,没有任何收益。

另请参阅

  • arch-event-driven — 事件通常会将写入传播到读模型投影。
  • arch-event-sourcing — 当写入端将事件作为事实来源存储时,CQRS 自然形成读取端。
  • ddd-aggregate — CQRS 写模型的典型形态。
  • ddd-repository — 读取端通常不会通过领域仓储读取;它们使用独立的查询对象。

参考资料

  • Khononov,Learning DDD,第 8 章(CQRS)。
  • Greg Young,“CQRS Documents by Greg Young”——对 CQRS 的最初阐述。
  • Vaughn Vernon,Implementing Domain-Driven Design——第 4 章介绍了 CQRS。

评论

支持 Markdown