CQRS与传统开发模式对比

概述

文档基于项目中的实际代码实现,对比分析CQRS(Command Query Responsibility Segregation)模式与传统模式的差异。通过具体的代码示例和架构分析,帮助开发者理解两种模式的适用场景和实现方式。

架构模式对比

传统CRUD模式架构

Controller → Service → Repository → Database
    ↓
统一的数据模型处理读写操作

特点:

  • 单一数据模型
  • 读写操作共享同一套逻辑
  • 直接的三层架构
  • 简单直观的数据流

事件驱动CQRS模式架构

Controller → MessageBus → CommandHandler/QueryHandler  
                    ↓
              Event Publishing
                    ↓
              Event Handlers    →  Database
                    ↓
            日志/缓存/通知/审计

特点:

  • 命令查询分离(Command Query Responsibility Segregation)
  • 事件驱动处理(Event-Driven Processing)
  • 消息总线协调(Message Bus Coordination)
  • 异步处理能力(Asynchronous Processing)

代码实现

事件驱动Command/Query Handler

@Service
public class CommandHandler implements MessageHandler<Command, ResponseDto> {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseDto handle(Command command) {
    }
}

@Service
public class QueryHandler implements MessageHandler<Query, Boolean> {
    
    @Override
    public Boolean handle(Query query) {
        // 专门处理查询逻辑
    }
}

消息对象设计

Command对象

@Getter
public class Command extends Message implements Message.Command {
}

Query对象

@Getter
public class Query extends Message implements Message.Query {
    
}

4. 消息总线实现

统一消息总线

@Slf4j
@Service
@RequiredArgsConstructor
public class MessageBus {
    
    private final ApplicationContext applicationContext;
    private final Map<Class<?>, Object> handlers = new HashMap<>();
    
    @PostConstruct
    public void initHandlers() {
        // 自动注册MessageHandler实现类
        applicationContext.getBeansOfType(MessageHandler.class).values()
            .forEach(handler -> {
                try {
                    Class<?> messageType = handler.getMessageType();
                    if (messageType != null) {
                        handlers.put(messageType, handler);
                        log.info("注册事件处理器: {} -> {}", 
                                messageType.getSimpleName(), 
                                handler.getClass().getSimpleName());
                    }
                } catch (Exception e) {
                    log.warn("注册处理器失败: {}", handler.getClass().getSimpleName(), e);
                }
            });
        
        log.info("消息总线初始化完成,共注册 {} 个处理器", handlers.size());
    }

    @SneakyThrows
    public <T, R> R send(T message) {
        log.debug("发送消息: {}", message.getClass().getSimpleName());
        
        // 验证消息类型
        validateMessage(message);
        
        Object handler = handlers.get(message.getClass());
        if (handler == null) {
            throw new IllegalArgumentException("未找到处理器: " + message.getClass().getSimpleName());
        }
        
        // 调用处理器
        R result = (R) handler.getClass()
                .getMethod("handle", message.getClass())
                .invoke(handler, message);

        log.debug("消息处理完成: {}", message.getClass().getSimpleName());
        return result;
    }
}

核心差异分析

职责分离

对比维度 传统CRUD 事件驱动CQRS
数据模型 读写共享统一模型 命令查询分离模型
业务逻辑 Service层混合处理 Handler专门处理
控制器职责 直接调用Service 消息分发协调
扩展性 修改现有代码 新增Handler/Strategy

性能对比

时间复杂度

操作类型 传统CRUD 事件驱动CQRS
路由分发 O(1) 直接调用 O(1) Map查找
业务逻辑 O(n) if-else链 O(1) 策略模式
扩展新功能 O(n) 修改现有代码 O(1) 新增Handler

内存使用

  • 传统CRUD: 较少对象创建,内存占用相对较小
  • 事件驱动CQRS: 更多对象创建(Command/Query/Event),但提供更好的可追踪性

核心差异总结

  1. 架构复杂度: CQRS > CRUD
  2. 开发效率: CRUD > CQRS(短期),CQRS > CRUD(长期)
  3. 可维护性: CQRS > CRUD
  4. 性能表现: CQRS > CRUD(复杂业务),CRUD > CQRS(简单操作)
  5. 学习成本: CRUD < CQRS

推荐资源

  • 《微服务架构设计模式》- CQRS和事件驱动架构详解
  • 《领域驱动设计》- DDD与CQRS的结合应用
  • 《构建微服务》- 事件驱动架构在微服务中的实践
  • 《软件架构师应该知道的97件事》- 架构决策和权衡

-end-

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容