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

一、DDD中的事务边界设计

1. 聚合根的事务一致性

在领域驱动设计中,聚合根是事务一致性的基本单元。一个事务应当只修改一个聚合根内的状态,跨聚合的操作应通过领域事件实现最终一致性。

// 订单聚合根示例
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 interface DomainEventPublisher {
    void publish(DomainEvent event);
}

// Spring实现示例
@Service
@Transactional
public class OrderService {
    @Autowired
    private DomainEventPublisher publisher;
    
    public void createOrder(Order order) {
        orderRepository.save(order);
        publisher.publish(new OrderCreatedEvent(order.getId()));
    }
}

二、微服务事务模式

1. Saga模式实现

Saga模式通过一系列本地事务和补偿操作实现分布式事务:

// Saga协调器示例
public class OrderSaga {
    @SagaStart
    public void handle(OrderCreatedEvent event) {
        // 步骤1: 预留库存
        sagaService.step()
            .invoke(() -> inventoryClient.reserve(event.getItems()))
            .withCompensation(() -> inventoryClient.cancelReserve(event.getItems()))
            
        // 步骤2: 创建支付
        .step()
            .invoke(() -> paymentClient.create(event.getOrderId(), event.getAmount()))
            .withCompensation(() -> paymentClient.cancel(event.getOrderId()))
            
        // 步骤3: 发货
        .step()
            .invoke(() -> shippingClient.ship(event.getOrderId()))
            .build()
            .execute();
    }
}

Saga实践要点

  • 每个步骤必须有对应的补偿操作
  • 使用持久化存储记录Saga状态
  • 实现幂等性以处理重试

2. TCC补偿事务

TCC(Try-Confirm-Cancel)模式示例:

图2

Java实现关键点

public interface TccAction {
    boolean tryPhase();
    boolean confirm();
    boolean cancel();
}

// 库存服务TCC实现
@Service
public class InventoryTccAction implements TccAction {
    @Override
    public boolean tryPhase() {
        // 冻结库存记录状态为"冻结"
        return inventoryDao.freeze(itemId, quantity) > 0;
    }
    
    @Override
    public boolean confirm() {
        // 实际扣减库存
        return inventoryDao.reduce(itemId, quantity) > 0;
    }
    
    @Override
    public boolean cancel() {
        // 释放冻结的库存
        return inventoryDao.release(itemId, quantity) > 0;
    }
}

3. 本地消息表

可靠消息的本地事务方案:

// 消息发送服务
@Service
@Transactional
public class MessageRelayService {
    @Autowired
    private MessageRepository messageRepo;
    
    public void saveAndSend(String topic, Object message) {
        // 1. 存储消息到本地表
        MessageRecord record = new MessageRecord(topic, JSON.toJSONString(message));
        messageRepo.save(record);
        
        // 2. 发送到MQ(如果失败会随事务回滚)
        mqProducer.send(topic, message);
    }
    
    @Scheduled(fixedRate = 5000)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void retryFailedMessages() {
        List<MessageRecord> failed = messageRepo.findByStatus(MessageStatus.FAILED);
        failed.forEach(record -> {
            try {
                mqProducer.send(record.getTopic(), record.getContent());
                record.markAsSent();
                messageRepo.save(record);
            } catch (Exception e) {
                log.error("重试消息发送失败", e);
            }
        });
    }
}

三、CQRS架构下的事务分离

1. 读写分离架构

图3

实现示例

// 命令侧
@Service
@Transactional
public class OrderCommandService {
    public void createOrder(OrderCommand command) {
        Order order = new Order(command);
        orderRepository.save(order);
        eventPublisher.publish(new OrderCreatedEvent(order));
    }
}

// 查询侧
@Service
@Transactional(readOnly = true)
public class OrderQueryService {
    public OrderView getOrder(String orderId) {
        return orderViewRepository.findById(orderId);
    }
}

2. 事件溯源实现

// 事件存储实现
public class EventSourcedOrder {
    private List<DomainEvent> changes = new ArrayList<>();
    
    public void apply(DomainEvent event) {
        this.changes.add(event);
        // 应用事件到当前状态
        when(event);
    }
    
    private void when(OrderCreated event) {
        this.id = event.getOrderId();
        this.status = OrderStatus.CREATED;
    }
    
    public static EventSourcedOrder recreate(List<DomainEvent> history) {
        EventSourcedOrder order = new EventSourcedOrder();
        history.forEach(order::when);
        return order;
    }
}

四、实践建议总结

  1. 事务设计原则

    • 能不用分布式事务就不用
    • 优先考虑最终一致性
    • 补偿机制必须完备
  2. 性能优化方向

图4

  1. 监控指标

    • 事务成功率/失败率
    • 平均事务处理时间
    • Saga/CQRS事件延迟
    • 死锁发生率

通过合理运用DDD的事务边界设计和微服务事务模式,可以在保持系统松耦合的同时,满足业务数据一致性的要求。建议根据具体业务场景选择合适的事务策略,并在可靠性和性能之间取得平衡。

添加新评论