概述
文档基于项目中的实际代码实现,对比分析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),但提供更好的可追踪性
核心差异总结
- 架构复杂度: CQRS > CRUD
- 开发效率: CRUD > CQRS(短期),CQRS > CRUD(长期)
- 可维护性: CQRS > CRUD
- 性能表现: CQRS > CRUD(复杂业务),CRUD > CQRS(简单操作)
- 学习成本: CRUD < CQRS
推荐资源
- 《微服务架构设计模式》- CQRS和事件驱动架构详解
- 《领域驱动设计》- DDD与CQRS的结合应用
- 《构建微服务》- 事件驱动架构在微服务中的实践
- 《软件架构师应该知道的97件事》- 架构决策和权衡
-end-