CQRS模式:命令查询职责分离的架构艺术
"读和写是不同的操作,为什么要用同一个模型来处理?"
在传统的CRUD应用中,我们通常使用同一个数据模型来handle读和写操作。然而,随着系统复杂度提升,这种方式会带来性能瓶颈、领域逻辑混乱等问题。CQRS(Command Query Responsibility Segregation) 模式应运而生,它将读操作和写操作分离,让系统更灵活、更高效。
什么是CQRS?
CQRS核心思想很简单:将读操作(Query)和写操作(Command)分开处理。
- Command(命令):修改系统状态的操作,如创建、更新、删除
- Query(查询):只读取数据,不改变状态
传统的架构是:
用户 → CRUD Model → 数据库
CQRS架构是:
用户 → Command Model → 数据库
↘ Query Model → 数据库
CQRS的核心优势
1. 性能优化
读和写的性能需求往往不同:
- 读操作可能需要复杂的联表查询、聚合
- 写操作需要严格的业务验证
分离后,可以分别为读和写优化:
- 读端:使用物化视图、缓存、反范式化
- 写端:保持规范的领域模型
2. 领域模型清晰
写模型(Command Model)专注于业务逻辑,保持领域的纯粹性
读模型(Query Model)专为展示需求定制,不受业务规则污染
3. 可扩展性强
- 读负载高?独立扩展读服务
- 写负载高?独立扩展写服务
- 无需互相影响
4. 支持复杂业务场景
- 写操作需要严格的验证和业务流程
- 读操作可能需要聚合多个数据源
CQRS实现模式
模式一:同步CQRS(最简单)
读写共用同一个数据库,但在应用层分离
模式二:事件驱动CQRS(推荐)
使用事件溯源(Event Sourcing),写操作产生事件,读端通过订阅事件更新物化视图
实战代码示例
定义Command和Query模型
# Command Model - 写模型
class CreateOrderCommand:
def __init__(self, customer_id, items):
self.customer_id = customer_id
self.items = items
def validate(self):
if not self.items:
raise ValueError("订单不能为空")
return True
# Query Model - 读模型
class OrderDetailQuery:
def __init__(self, order_id):
self.order_id = order_id
Command Handler
class OrderCommandHandler:
def __init__(self, event_store):
self.event_store = event_store
def handle_create_order(self, command: CreateOrderCommand):
command.validate()
# 创建订单事件
event = OrderCreatedEvent(
customer_id=command.customer_id,
items=command.items,
timestamp=datetime.now()
)
self.event_store.append(event)
return event.order_id
Query Handler
class OrderQueryHandler:
def __init__(self, read_database):
self.read_db = read_database
def get_order_detail(self, query: OrderDetailQuery):
# 直接从物化视图读取,不需要复杂的聚合
return self.read_db.orders.find_one(
{"order_id": query.order_id}
)
def get_customer_orders(self, customer_id):
# 为读取优化的查询
return list(self.read_db.order_summary.find(
{"customer_id": customer_id}
))
CQRS的挑战与注意事项
1. 数据一致性
- 读写分离后,数据同步需要时间(最终一致性)
- 需要权衡:实时性要求高的场景慎用
2. 复杂度增加
- 两套模型需要维护
- 事件驱动模式需要处理事件幂等、补偿等
3. 不适用于所有场景
- 简单CRUD应用不需要CQRS
- 读写比例接近的系统收益不大
- 团队经验不足时慎用
4. 建议结合DDD使用
CQRS与领域驱动设计天然契合:
- Command对应Domain Command
- Query端可以有自己的Read Model
- 事件驱动是DDD常用的技术手段
什么时候使用CQRS?
✅ 推荐使用:
- 复杂业务逻辑的企业应用
- 读写负载差异明显的系统
- 需要高性能读取的场景(如报表、仪表盘)
- 基于DDD的微服务架构
❌ 不建议使用:
- 简单的CRUD应用
- 强一致性要求极高的场景
- 团队对模式不熟悉
总结
CQRS不是银弹,但它为复杂系统提供了一种优雅的架构选择。通过将读和写分离,我们可以:
- 获得更好的性能优化空间
- 保持领域模型的清晰
- 构建更具可扩展性的系统
记住:架构模式是为了解决问题,而不是为了使用而使用。在合适的场景下,CQRS会成为你构建高质量系统的有力工具。
本文是软件架构系列文章的一部分,如果你对DDD、六边形架构等话题感兴趣,欢迎关注!
Top comments (0)