DDD与微服务事务架构设计实践指南
领域驱动设计与微服务事务架构实践
一、DDD中的事务边界设计
1. 聚合根的事务一致性
概念解释:
在DDD中,聚合根(Aggregate Root)是事务一致性的基本单元。一个事务应当只修改单个聚合根及其内部实体,跨聚合的操作应通过最终一致性实现。
// 订单聚合根示例
public class Order {
private OrderId id;
private List<OrderItem> items;
private Money totalAmount;
// 聚合内部方法保持事务一致性
public void addItem(Product product, int quantity) {
OrderItem item = new OrderItem(product, quantity);
this.items.add(item);
this.totalAmount = calculateTotal();
}
}
实践建议:
- 保持聚合尽量小(通常不超过10个对象)
- 通过工厂方法或构造函数保证聚合的完整性
- 避免在聚合中直接引用其他聚合,使用ID关联
2. 领域事件与最终一致性
实现模式:
代码示例:
// 领域事件发布
public class OrderService {
@Transactional
public void createOrder(OrderCommand command) {
Order order = new Order(...);
orderRepository.save(order);
eventPublisher.publish(new OrderCreatedEvent(order.getId()));
}
}
// 事件处理器
@Service
public class PaymentEventHandler {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(OrderCreatedEvent event) {
// 处理支付逻辑
}
}
实践建议:
- 使用事务日志追踪模式(Transaction Log Tailing)确保事件发布
- 实现幂等处理防止重复消费
- 设置合理的事件过期时间
二、微服务事务模式实现
1. Saga模式实现
Saga类型对比:
类型 | 协调方式 | 复杂度 | 适用场景 |
---|---|---|---|
编排式 | 分布式 | 高 | 复杂业务流程 |
协同式 | 中心化 | 低 | 简单流程 |
编排式Saga示例:
补偿事务实现:
public class OrderSaga {
@SagaStart
public void handle(OrderCreatedEvent event) {
// 1. 扣减库存
inventoryService.reserveStock(...);
// 2. 扣款
paymentService.debitAccount(...);
}
@SagaEnd
public void handle(PaymentCompletedEvent event) {
// Saga完成
}
@Compensate
public void compensate(OrderCancelledEvent event) {
// 补偿逻辑
inventoryService.cancelReservation(...);
paymentService.refund(...);
}
}
2. TCC补偿事务
三阶段实现:
// Try阶段
@Transactional
public boolean tryReserveInventory(Long productId, int quantity) {
// 预占库存
return inventoryDao.reserve(productId, quantity) > 0;
}
// Confirm阶段
public void confirmReserve(Long productId, int quantity) {
inventoryDao.confirmReservation(productId, quantity);
}
// Cancel阶段
public void cancelReserve(Long productId, int quantity) {
inventoryDao.cancelReservation(productId, quantity);
}
实践建议:
- 实现幂等接口
- 设置事务过期时间
- 记录事务日志用于恢复
3. 本地消息表
实现架构:
关键代码:
@Transactional
public void placeOrder(Order order) {
// 1. 保存订单
orderDao.insert(order);
// 2. 写入本地消息
LocalMessage message = new LocalMessage();
message.setContent(toJson(order));
message.setStatus(PENDING);
messageDao.insert(message);
}
// 定时任务
@Scheduled(fixedRate = 5000)
public void processPendingMessages() {
List<LocalMessage> messages = messageDao.selectPending();
messages.forEach(msg -> {
try {
mqProducer.send(msg.getContent());
messageDao.updateStatus(msg.getId(), SENT);
} catch (Exception e) {
log.error("消息发送失败", e);
}
});
}
三、CQRS架构下的事务分离
1. 读写分离实现
架构设计:
事务处理要点:
- 命令端使用强一致性事务
- 查询端最终一致性更新
- 通过事件溯源(Event Sourcing)重建状态
2. 读模型更新策略
更新模式对比:
策略 | 延迟 | 复杂度 | 数据一致性 |
---|---|---|---|
同步更新 | 低 | 高 | 强 |
异步批处理 | 高 | 中 | 最终 |
事件驱动 | 中 | 中 | 最终 |
实践建议:
- 关键业务数据采用同步双写
- 非关键数据采用事件驱动更新
- 实现读模型重建机制
四、性能优化实践
批量处理优化:
// JDBC批量插入 try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(...)) { conn.setAutoCommit(false); for (OrderItem item : items) { stmt.setLong(1, item.getProductId()); stmt.setInt(2, item.getQuantity()); stmt.addBatch(); } stmt.executeBatch(); conn.commit(); }
连接池配置:
# HikariCP配置示例 spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000
长事务拆分:
// 使用状态机拆分长事务 public void processLargeOrder(Order order) { orderService.create(order); asyncTaskExecutor.execute(() -> { // 异步处理后续步骤 inventoryService.reserve(order); paymentService.process(order); }); }
总结建议
事务边界设计原则:
- 聚合内强一致性,聚合间最终一致性
- 一个事务只修改一个聚合根
- 通过领域事件驱动业务流程
微服务事务选型指南:
if (业务流程短且简单) { 使用本地消息表 } else if (需要强一致性) { 考虑2PC(性能敏感慎用) } else if (业务流程长) { 采用Saga模式 } else if (需要柔性事务) { 选择TCC模式 }
性能关键点:
- 减少分布式事务范围
- 优化批量操作
- 合理设置超时时间
- 实现完善的监控告警