1. CQRS 模式概述:定义、历史与适用边界
1.1 什么是 CQRS?
CQRS(Command Query Responsibility Segregation,命令查询职责分离)是一种将写入(命令)与读取(查询)分离到不同模型中的架构模式。其核心思想是:一个方法要么是执行操作的命令,要么是返回数据的查询,不应同时兼具两种职责。
一句话定义:用不同的模型分别处理“写”和“读”,而不是让同一个模型既负责数据变更又负责数据展示。
1.2 历史背景
CQRS 的概念最早由 Greg Young 在 2010 年前后提出,其思想根源可追溯至 Command-Query Separation(CQS) 原则——由 Bertrand Meyer 在《面向对象软件构造》中提出。CQS 要求每个方法要么是命令(改变状态),要么是查询(返回结果),但不能两者皆是。
CQRS 将 CQS 从方法级别提升到模型级别,并常与事件溯源(Event Sourcing)、领域驱动设计(DDD)结合使用,成为构建复杂业务系统的关键模式之一。
1.3 CQRS 与 CRUD 的核心区别
| 维度 | CRUD | CQRS |
|---|---|---|
| 模型数量 | 单一模型同时处理读写 | 命令模型 + 查询模型,两个独立模型 |
| 返回类型 | 写操作通常返回实体数据 | 命令不返回数据(或仅返回 ID/状态),查询返回视图 |
| 数据一致性 | 强一致性(同一数据源) | 最终一致性(写模型与读模型可异步同步) |
| 性能优化 | 读写争用同一资源,难以独立优化 | 读写可独立扩展、独立优化存储 |
| 复杂度 | 低,适合简单场景 | 较高,需要处理同步、版本、一致性 |
| 典型代表 | RESTful API + ORM 直接操作数据库 | 命令总线 + 事件总线 + 物化视图 |
关键差异:CRUD 用一个模型做所有事,CQRS 用两个模型各司其职。CQRS 不是 CRUD 的替代品,而是针对复杂领域的升级方案。
1.4 CQRS 核心架构图
1.5 适用场景
- 复杂业务领域:领域逻辑复杂,写操作涉及多个聚合、多个限界上下文,需要独立建模。
- 读写负载不均衡:读请求远多于写请求,或写请求远多于读请求,需要独立优化。
- 多端展示需求:同一数据需要以不同视图呈现(如 Web 端、移动端、报表端),每个视图的查询模型可独立定制。
- 与事件溯源结合:需要记录所有状态变更历史,命令侧存储事件流,查询侧构建投影。
- 团队分工明确:写模型团队与读模型团队可并行开发,互不阻塞。
典型例子:电商订单系统——下单(命令)涉及库存、支付、物流多个聚合,而订单列表查询(查询)只需从物化视图快速读取,无需经过复杂领域逻辑。
1.6 不适用场景
| 场景 | 原因 |
|---|---|
| 简单 CRUD 应用 | 增加不必要的复杂度,单一模型即可满足需求 |
| 强一致性要求极高 | CQRS 默认最终一致性,实现强一致性需要额外机制(如分布式事务),成本高 |
| 读写模型几乎相同 | 如果命令模型与查询模型结构高度相似,分离带来的收益微乎其微 |
| 小团队 / 快速原型 | 维护两套模型、同步机制、事件处理会增加开发与运维负担 |
| 无事件溯源需求 | 如果不需要记录完整变更历史,CQRS 的优势被削弱 |
避坑提示:不要为了“用模式而用模式”。如果项目只有几张表、几十个接口,CRUD 是最佳选择。CQRS 是为复杂领域准备的,不是银弹。
1.7 适用性决策流程图
1.8 小结
CQRS 是一种读写模型分离的架构模式,适用于复杂业务领域、读写负载不均衡、多端展示等场景。它与 CRUD 并非对立,而是针对不同复杂度层次的选择。在 Go 语言中实现 CQRS 时,通常结合 DDD 聚合、事件总线和物化视图,构建清晰、可扩展的领域模型。