CQRS 模式实现指南

CQRS(Command Query Responsibility Segregation,命令查询职责分离)是一种架构模式,它将数据修改操作(命令)和数据读取操作(查询)分离,使用不同的模型来处理。

基本概念

CQRS 的核心思想是将系统分为两部分:

  • 命令端:处理创建、更新和删除操作(写操作)

  • 查询端:处理数据读取操作(读操作)

实现方式

1. 简单分离

最基本的 CQRS 实现只需在代码层面分离命令和查询:

// 命令
public class ProductCommandService
{
    public void CreateProduct(Product product) { /* 实现 */ }
    public void UpdateProduct(Product product) { /* 实现 */ }
}

// 查询
public class ProductQueryService
{
    public Product GetProductById(int id) { /* 实现 */ }
    public IEnumerable GetAllProducts() { /* 实现 */ }
}

2. 分离数据存储

更高级的实现可以使用不同的数据存储:

// 命令端使用关系型数据库
public class ProductCommandRepository
{
    private readonly SqlDbContext _context;
    
    public void Add(Product product)
    {
        _context.Products.Add(product);
        _context.SaveChanges();
    }
}

// 查询端使用NoSQL或缓存
public class ProductQueryRepository
{
    private readonly MongoDbContext _context;
    
    public Product GetById(int id)
    {
        return _context.Products.Find(p => p.Id == id).FirstOrDefault();
    }
}

3. 事件溯源实现

结合事件溯源的 CQRS 实现:

// 命令端
public class ProductCommandHandler
{
    private readonly IEventStore _eventStore;
    
    public void Handle(CreateProductCommand command)
    {
        var product = new Product(command.Id, command.Name, command.Price);
        var events = product.GetUncommittedChanges();
        _eventStore.SaveEvents(product.Id, events);
    }
}

// 查询端
public class ProductView
{
    private readonly IReadModelDatabase _database;
    
    public ProductDto GetProduct(int id)
    {
        return _database.Get(id);
    }
}

// 投影(同步命令和查询模型)
public class ProductProjection
{
    public void Apply(ProductCreatedEvent @event)
    {
        // 更新查询模型
        _database.Save(new ProductDto {
            Id = @event.Id,
            Name = @event.Name,
            Price = @event.Price
        });
    }
}

实现考虑因素

  1. 一致性模型

    • 强一致性 vs 最终一致性

    • 决定查询端何时反映命令端的更改

  2. 同步机制

    • 数据库触发器

    • 消息队列/事件总线

    • 定时批处理

  3. 性能优化

    • 查询端可以针对读取优化(如非规范化)

    • 命令端可以针对写入优化

  4. 复杂性管理

    • 只有复杂领域才需要完整CQRS

    • 简单CRUD应用可能不需要

优缺点

优点

  • 读写操作可以独立扩展

  • 优化查询性能(非规范化)

  • 更清晰的分离关注点

  • 更适合复杂业务逻辑

缺点

  • 增加了系统复杂性

  • 需要处理数据一致性

  • 学习曲线较陡

适用场景

  • 高并发系统,读写负载差异大

  • 复杂领域模型

  • 需要灵活扩展查询功能的系统

  • 需要审计日志或历史追踪的系统

CQRS 不是银弹,应根据具体需求决定是否采用。对于简单应用,传统的CRUD可能更合适。

你可能感兴趣的:(开发语言)