领域驱动设计与微服务事务架构实践

一、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. 领域事件与最终一致性

实现模式

图1

代码示例

// 领域事件发布
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示例

图2

补偿事务实现

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. 本地消息表

实现架构

图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. 读写分离实现

架构设计

图4

事务处理要点

  • 命令端使用强一致性事务
  • 查询端最终一致性更新
  • 通过事件溯源(Event Sourcing)重建状态

2. 读模型更新策略

更新模式对比

策略延迟复杂度数据一致性
同步更新
异步批处理最终
事件驱动最终

实践建议

  • 关键业务数据采用同步双写
  • 非关键数据采用事件驱动更新
  • 实现读模型重建机制

四、性能优化实践

  1. 批量处理优化

    // 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();
    }
  2. 连接池配置

    # HikariCP配置示例
    spring:
      datasource:
     hikari:
       maximum-pool-size: 20
       minimum-idle: 5
       connection-timeout: 30000
       idle-timeout: 600000
       max-lifetime: 1800000
  3. 长事务拆分

    // 使用状态机拆分长事务
    public void processLargeOrder(Order order) {
     orderService.create(order);
     asyncTaskExecutor.execute(() -> {
         // 异步处理后续步骤
         inventoryService.reserve(order);
         paymentService.process(order);
     });
    }

总结建议

  1. 事务边界设计原则

    • 聚合内强一致性,聚合间最终一致性
    • 一个事务只修改一个聚合根
    • 通过领域事件驱动业务流程
  2. 微服务事务选型指南

    if (业务流程短且简单) {
        使用本地消息表
    } else if (需要强一致性) {
        考虑2PC(性能敏感慎用)
    } else if (业务流程长) {
        采用Saga模式
    } else if (需要柔性事务) {
        选择TCC模式
    }
  3. 性能关键点

    • 减少分布式事务范围
    • 优化批量操作
    • 合理设置超时时间
    • 实现完善的监控告警

添加新评论