Skip to content

CQRS架构(Command Query Responsibility Segregation)

概述

CQRS(Command Query Responsibility Segregation,命令查询职责分离)是一种架构模式,它将系统的读操作(查询)和写操作(命令)分离到不同的模型中。这种分离允许我们针对读和写的不同特性进行独立的优化,从而提高系统的性能、可扩展性和可维护性。

CQRS的核心思想来源于CQS(Command Query Separation)原则,但将其扩展到了架构层面。在CQRS中,命令负责改变系统状态,查询负责返回系统状态,两者使用不同的模型和可能不同的数据存储。

架构图

基本CQRS架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                           CQRS架构                                          │
└─────────────────────────────────────────────────────────────────────────────┘

客户端请求


┌─────────────────┐
│   API网关       │
└─────────┬───────┘

    ┌─────┴─────┐
    │           │
    ▼           ▼
┌─────────┐ ┌─────────┐
│ 命令API │ │ 查询API │
└─────────┘ └─────────┘
    │           │
    ▼           ▼
┌─────────────────┐ ┌─────────────────┐
│   命令端        │ │   查询端        │
│                │ │                │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 命令处理器  │ │ │ │ 查询处理器  │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 领域模型    │ │ │ │ 查询模型    │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 写入数据库  │ │ │ │ 读取数据库  │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘

CQRS + 事件溯源架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                      CQRS + 事件溯源架构                                    │
└─────────────────────────────────────────────────────────────────────────────┘

命令端:
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   命令API   │───▶│ 命令处理器  │───▶│  聚合根     │───▶│  事件存储   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘



                                                       ┌─────────────┐
                                                       │  事件总线   │
                                                       └─────────────┘



查询端:
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   查询API   │◄───│ 查询处理器  │◄───│  投影处理器 │◄───│  事件订阅   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
                          │                   │
                          ▼                   ▼
                   ┌─────────────┐    ┌─────────────┐
                   │  查询模型   │    │  读取数据库 │
                   └─────────────┘    └─────────────┘

多读模型CQRS架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                        多读模型CQRS架构                                     │
└─────────────────────────────────────────────────────────────────────────────┘

                    ┌─────────────┐
                    │   命令端    │
                    │            │
                    │ 写入数据库  │
                    └─────┬───────┘

                          │ 数据变更事件

                    ┌─────────────┐
                    │  事件总线   │
                    └─────┬───────┘

        ┌─────────────────┼─────────────────┐
        │                 │                 │
        ▼                 ▼                 ▼
┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│  列表视图   │   │  详情视图   │   │  统计视图   │
│            │   │            │   │            │
│ ┌─────────┐ │   │ ┌─────────┐ │   │ ┌─────────┐ │
│ │关系数据库│ │   │ │文档数据库│ │   │ │时序数据库│ │
│ └─────────┘ │   │ └─────────┘ │   │ └─────────┘ │
│ ┌─────────┐ │   │ ┌─────────┐ │   │ ┌─────────┐ │
│ │查询API1 │ │   │ │查询API2 │ │   │ │查询API3 │ │
│ └─────────┘ │   │ └─────────┘ │   │ └─────────┘ │
└─────────────┘   └─────────────┘   └─────────────┘

核心概念

1. 命令(Commands)

定义: 命令是表达用户意图的对象,它描述了系统应该执行的操作。命令是改变系统状态的唯一方式。

特征:

  • 意图表达:命令表达用户的意图而不是数据
  • 动词命名:使用动词形式命名(如CreateOrder、UpdateUser)
  • 包含参数:包含执行操作所需的所有参数
  • 不返回数据:命令不返回业务数据,只返回执行结果

命令结构:

┌─────────────────────────────────────────┐
│              CreateOrderCommand          │
│ ┌─────────────────────────────────────┐ │
│ │ CommandId: uuid-1234                │ │
│ │ UserId: user-5678                   │ │
│ │ ProductId: product-9999             │ │
│ │ Quantity: 2                         │ │
│ │ Price: 99.99                        │ │
│ │ Timestamp: 2024-01-15T10:30:00Z     │ │
│ │ CorrelationId: req-1111             │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘

2. 查询(Queries)

定义: 查询是获取系统状态信息的操作,它不会改变系统状态,只返回数据。

特征:

  • 只读操作:不改变系统状态
  • 返回数据:返回客户端需要的数据
  • 可缓存:查询结果可以被缓存
  • 幂等性:多次执行相同查询返回相同结果

查询结构:

┌─────────────────────────────────────────┐
│            GetOrdersByUserQuery          │
│ ┌─────────────────────────────────────┐ │
│ │ QueryId: uuid-5678                  │ │
│ │ UserId: user-1234                   │ │
│ │ Status: ["Pending", "Confirmed"]    │ │
│ │ DateFrom: 2024-01-01                │ │
│ │ DateTo: 2024-01-31                  │ │
│ │ PageSize: 20                        │ │
│ │ PageNumber: 1                       │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘

3. 命令处理器(Command Handlers)

职责:

  • 接收和验证命令
  • 执行业务逻辑
  • 更新领域模型
  • 持久化状态变更
  • 发布领域事件

处理流程:

命令接收 ──▶ 命令验证 ──▶ 业务逻辑执行 ──▶ 状态更新 ──▶ 事件发布
    │           │           │             │           │
    ▼           ▼           ▼             ▼           ▼
  参数检查    业务规则    领域模型操作    数据持久化   事件通知

实现模式:

单一处理器模式

┌─────────────────┐
│ CreateOrderHandler │
│                │
│ handle()       │ ──▶ 处理CreateOrderCommand
└─────────────────┘

聚合处理器模式

┌─────────────────┐
│ OrderCommandHandler │
│                │
│ handleCreate() │ ──▶ 处理CreateOrderCommand
│ handleUpdate() │ ──▶ 处理UpdateOrderCommand
│ handleCancel() │ ──▶ 处理CancelOrderCommand
└─────────────────┘

4. 查询处理器(Query Handlers)

职责:

  • 接收和验证查询
  • 从读模型获取数据
  • 数据转换和格式化
  • 返回查询结果
  • 处理分页和排序

处理流程:

查询接收 ──▶ 参数验证 ──▶ 数据获取 ──▶ 数据转换 ──▶ 结果返回
    │           │           │           │           │
    ▼           ▼           ▼           ▼           ▼
  查询解析    参数检查    数据库查询   格式转换    响应构建

优化策略:

缓存策略

查询请求 ──▶ 缓存检查 ──▶ 数据库查询 ──▶ 缓存更新 ──▶ 结果返回
    │           │           │           │           │
    ▼           ▼           ▼           ▼           ▼
  请求解析    缓存命中    数据获取    缓存存储    响应返回

预计算策略

数据变更 ──▶ 触发计算 ──▶ 结果存储 ──▶ 查询获取 ──▶ 快速返回
    │           │           │           │           │
    ▼           ▼           ▼           ▼           ▼
  事件监听    异步计算    预存结果    直接读取    即时响应

5. 读写模型分离

写模型(Write Model):

  • 面向业务逻辑和规则
  • 强调数据一致性
  • 复杂的关系和约束
  • 规范化的数据结构
  • 支持事务操作

读模型(Read Model):

  • 面向查询和展示
  • 强调查询性能
  • 简化的数据结构
  • 反规范化的数据
  • 支持复杂查询

模型对比:

特性写模型读模型
目的业务逻辑执行数据查询展示
结构规范化反规范化
一致性强一致性最终一致性
性能写入优化读取优化
复杂性业务复杂查询复杂
变更频率相对稳定频繁变更

架构模式

1. 简单CQRS

特点:

  • 共享数据库
  • 不同的读写模型
  • 同步数据更新
  • 实现简单

适用场景:

  • 读写比例不均衡
  • 查询复杂度高
  • 团队技术能力有限
  • 项目初期阶段

架构图:

┌─────────────┐    ┌─────────────┐
│   命令端    │    │   查询端    │
│            │    │            │
│ 写模型      │    │ 读模型      │
└─────┬───────┘    └─────┬───────┘
      │                  │
      ▼                  ▼
┌─────────────────────────────┐
│        共享数据库           │
└─────────────────────────────┘

2. CQRS + 事件驱动

特点:

  • 分离的数据存储
  • 事件驱动的数据同步
  • 异步数据更新
  • 最终一致性

适用场景:

  • 高并发读写
  • 复杂的业务流程
  • 微服务架构
  • 实时数据处理

数据流:

命令 ──▶ 写模型 ──▶ 事件发布 ──▶ 事件处理 ──▶ 读模型更新
  │        │         │          │          │
  ▼        ▼         ▼          ▼          ▼
业务意图  状态变更   事件通知    数据同步   查询优化

3. CQRS + 事件溯源

特点:

  • 事件作为唯一数据源
  • 状态通过事件重建
  • 完整的审计日志
  • 时间旅行查询

适用场景:

  • 审计要求严格
  • 复杂的业务规则
  • 需要历史数据分析
  • 高度可追溯性

状态重建:

事件流 ──▶ 事件重放 ──▶ 状态重建 ──▶ 快照生成 ──▶ 查询服务
  │         │          │          │          │
  ▼         ▼          ▼          ▼          ▼
历史记录   顺序处理   状态计算   性能优化   快速查询

实施策略

1. 渐进式实施

第一阶段:读写分离

现有系统 ──▶ 添加查询模型 ──▶ 分离查询逻辑 ──▶ 优化查询性能
    │           │             │             │
    ▼           ▼             ▼             ▼
  单一模型    双重模型      逻辑分离      性能提升

第二阶段:命令分离

读写分离 ──▶ 添加命令模型 ──▶ 分离命令逻辑 ──▶ 优化写入性能
    │           │             │             │
    ▼           ▼             ▼             ▼
  查询优化    命令建模      逻辑分离      写入优化

第三阶段:事件集成

命令分离 ──▶ 引入事件机制 ──▶ 异步数据同步 ──▶ 最终一致性
    │           │             │             │
    ▼           ▼             ▼             ▼
  同步更新    事件发布      异步处理      一致性保证

2. 技术选型

命令端技术栈

应用框架:

  • Spring Boot (Java)
  • ASP.NET Core (C#)
  • Express.js (Node.js)
  • Django/FastAPI (Python)

数据存储:

  • 关系型数据库(PostgreSQL、MySQL)
  • 文档数据库(MongoDB)
  • 事件存储(EventStore)

消息中间件:

  • Apache Kafka
  • RabbitMQ
  • Apache Pulsar
  • Redis Streams

查询端技术栈

查询引擎:

  • Elasticsearch
  • Apache Solr
  • MongoDB
  • Redis

缓存系统:

  • Redis
  • Memcached
  • Hazelcast
  • Caffeine

数据处理:

  • Apache Spark
  • Apache Flink
  • Kafka Streams
  • Storm

3. 数据同步策略

实时同步

写操作 ──▶ 事件发布 ──▶ 实时处理 ──▶ 读模型更新
   │         │          │          │
   ▼         ▼          ▼          ▼
 状态变更   事件流     流处理     即时更新

批量同步

写操作 ──▶ 事件收集 ──▶ 批量处理 ──▶ 读模型更新
   │         │          │          │
   ▼         ▼          ▼          ▼
 状态变更   事件缓存   批处理     批量更新

混合同步

关键数据 ──▶ 实时同步 ──▶ 即时更新


非关键数据 ──▶ 批量同步 ──▶ 延迟更新

优势

1. 性能优化

读写分离:

  • 独立的性能优化
  • 不同的存储技术
  • 专门的查询优化
  • 缓存策略优化

并发处理:

  • 读写操作并行
  • 减少锁竞争
  • 提高系统吞吐量
  • 支持高并发访问

2. 可扩展性

独立扩展:

  • 读写端独立扩展
  • 按需分配资源
  • 弹性伸缩能力
  • 成本效益优化

技术多样性:

  • 不同技术栈选择
  • 最适合的存储方案
  • 专业化的优化
  • 技术演进灵活性

3. 复杂查询支持

查询优化:

  • 反规范化数据结构
  • 预计算和物化视图
  • 专门的查询引擎
  • 复杂分析查询

多视图支持:

  • 不同的数据视图
  • 多种查询模式
  • 个性化的数据展示
  • 灵活的报表生成

4. 业务逻辑清晰

关注点分离:

  • 命令专注业务逻辑
  • 查询专注数据展示
  • 清晰的职责边界
  • 易于理解和维护

模型独立:

  • 独立的模型演进
  • 减少相互影响
  • 灵活的变更管理
  • 降低耦合度

挑战与劣势

1. 复杂性增加

架构复杂性:

  • 多个数据模型
  • 数据同步机制
  • 一致性管理
  • 错误处理复杂

开发复杂性:

  • 双重开发工作
  • 数据映射和转换
  • 测试复杂度增加
  • 调试困难

2. 数据一致性

最终一致性:

  • 读写数据可能不一致
  • 业务逻辑复杂化
  • 用户体验影响
  • 错误处理困难

同步延迟:

  • 数据更新延迟
  • 实时性要求冲突
  • 业务流程影响
  • 用户期望管理

3. 运维挑战

监控复杂性:

  • 多个系统监控
  • 数据一致性监控
  • 性能指标复杂
  • 故障诊断困难

部署复杂性:

  • 多个组件部署
  • 版本兼容性
  • 回滚策略复杂
  • 配置管理困难

4. 学习成本

技术门槛:

  • 新的架构概念
  • 分布式系统知识
  • 事件驱动编程
  • 异步编程模型

团队能力:

  • 技能要求提高
  • 培训成本增加
  • 经验积累时间
  • 人员流动风险

适用场景

1. 读写比例不均衡

读多写少:

  • 内容管理系统
  • 电商产品目录
  • 新闻资讯平台
  • 知识库系统

写多读少:

  • 日志收集系统
  • 监控数据采集
  • IoT数据收集
  • 审计日志系统

2. 复杂查询需求

多维度查询:

  • 商业智能系统
  • 数据分析平台
  • 报表生成系统
  • 搜索引擎

实时分析:

  • 实时监控系统
  • 欺诈检测系统
  • 推荐系统
  • 个性化服务

3. 高性能要求

高并发访问:

  • 社交媒体平台
  • 在线游戏系统
  • 直播平台
  • 即时通讯系统

低延迟要求:

  • 金融交易系统
  • 实时竞价系统
  • 在线广告系统
  • 高频交易系统

4. 微服务架构

服务自治:

  • 独立的数据模型
  • 服务间解耦
  • 独立的技术选型
  • 灵活的部署策略

分布式系统:

  • 跨服务数据一致性
  • 服务间通信优化
  • 故障隔离
  • 弹性设计

最佳实践

1. 设计原则

单一职责:

  • 命令专注状态变更
  • 查询专注数据获取
  • 清晰的边界定义
  • 避免职责混合

接口隔离:

  • 独立的命令接口
  • 独立的查询接口
  • 最小化接口依赖
  • 灵活的接口设计

依赖倒置:

  • 抽象的处理器接口
  • 可插拔的存储实现
  • 松耦合的组件设计
  • 易于测试和扩展

2. 实施建议

渐进式迁移:

  • 从简单场景开始
  • 逐步增加复杂性
  • 积累经验和最佳实践
  • 降低实施风险

技术选型:

  • 根据业务需求选择
  • 考虑团队技术能力
  • 评估维护成本
  • 保持技术一致性

监控和运维:

  • 完善的监控体系
  • 自动化运维工具
  • 故障预警机制
  • 性能优化策略

3. 常见陷阱

过度设计:

  • 避免不必要的复杂性
  • 根据实际需求设计
  • 保持简单有效
  • 渐进式演进

一致性问题:

  • 明确一致性要求
  • 设计补偿机制
  • 处理数据冲突
  • 用户体验考虑

性能问题:

  • 合理的缓存策略
  • 优化数据同步
  • 监控性能指标
  • 及时性能调优

与其他架构的关系

CQRS vs 传统CRUD

特性CQRS传统CRUD
模型读写分离统一模型
复杂性
性能可独立优化统一优化
一致性最终一致强一致
扩展性中等
学习成本

与事件驱动架构的结合

事件作为桥梁:

  • 连接读写模型
  • 实现数据同步
  • 支持异步处理
  • 提供解耦机制

优势互补:

  • CQRS提供模型分离
  • 事件驱动提供通信机制
  • 共同实现高性能
  • 支持复杂业务流程

与微服务的结合

服务内CQRS:

  • 单个服务内的读写分离
  • 服务内的性能优化
  • 简化的实施复杂度
  • 渐进式的架构演进

跨服务CQRS:

  • 服务间的数据同步
  • 分布式的读写分离
  • 复杂的一致性管理
  • 高度的系统解耦

总结

CQRS架构通过将读写操作分离到不同的模型中,为系统提供了更好的性能、可扩展性和灵活性。它特别适合于读写比例不均衡、复杂查询需求、高性能要求和微服务架构的场景。

主要优势:

  1. 独立的性能优化空间
  2. 更好的可扩展性
  3. 支持复杂查询需求
  4. 清晰的业务逻辑分离

主要挑战:

  1. 架构和开发复杂性增加
  2. 数据一致性管理困难
  3. 运维和监控复杂
  4. 团队学习成本高

成功实施的关键:

  1. 明确的业务需求和技术目标
  2. 渐进式的实施策略
  3. 合适的技术选型
  4. 完善的监控和运维体系
  5. 团队的技术能力建设

选择CQRS架构时,需要权衡其带来的好处和增加的复杂性,确保团队有足够的能力来设计、实施和维护这种架构。建议从简单的读写分离开始,逐步演进到完整的CQRS架构,并结合事件驱动等其他架构模式以获得更好的效果。