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 核心架构图

CQRS 核心架构:命令模型与查询模型分离 命令侧(写模型) Command 命令对象 Command Handler 领域模型 / 聚合根 写数据库(事件存储) 查询侧(读模型) Query 查询对象 Query Handler 物化视图 / 读模型 读数据库(优化查询) 异步同步 用户 → 命令 用户 → 查询

1.5 适用场景

  • 复杂业务领域:领域逻辑复杂,写操作涉及多个聚合、多个限界上下文,需要独立建模。
  • 读写负载不均衡:读请求远多于写请求,或写请求远多于读请求,需要独立优化。
  • 多端展示需求:同一数据需要以不同视图呈现(如 Web 端、移动端、报表端),每个视图的查询模型可独立定制。
  • 与事件溯源结合:需要记录所有状态变更历史,命令侧存储事件流,查询侧构建投影。
  • 团队分工明确:写模型团队与读模型团队可并行开发,互不阻塞。
典型例子:电商订单系统——下单(命令)涉及库存、支付、物流多个聚合,而订单列表查询(查询)只需从物化视图快速读取,无需经过复杂领域逻辑。

1.6 不适用场景

场景 原因
简单 CRUD 应用 增加不必要的复杂度,单一模型即可满足需求
强一致性要求极高 CQRS 默认最终一致性,实现强一致性需要额外机制(如分布式事务),成本高
读写模型几乎相同 如果命令模型与查询模型结构高度相似,分离带来的收益微乎其微
小团队 / 快速原型 维护两套模型、同步机制、事件处理会增加开发与运维负担
无事件溯源需求 如果不需要记录完整变更历史,CQRS 的优势被削弱
避坑提示:不要为了“用模式而用模式”。如果项目只有几张表、几十个接口,CRUD 是最佳选择。CQRS 是为复杂领域准备的,不是银弹。

1.7 适用性决策流程图

是否采用 CQRS?—— 快速决策流程 开始评估 领域逻辑复杂? 读写负载不均衡? 需要多视图? 需要事件溯源? ✅ 适合 CQRS ❌ 保持 CRUD 是 否

1.8 小结

CQRS 是一种读写模型分离的架构模式,适用于复杂业务领域、读写负载不均衡、多端展示等场景。它与 CRUD 并非对立,而是针对不同复杂度层次的选择。在 Go 语言中实现 CQRS 时,通常结合 DDD 聚合、事件总线和物化视图,构建清晰、可扩展的领域模型。