DDD与微服务事务架构实践指南
领域驱动设计与微服务事务架构实践
一、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. 领域事件与最终一致性
领域事件是实现跨聚合一致性的关键机制:
事件存储实现示例:
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)模式示例:
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. 读写分离架构
实现示例:
// 命令侧
@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;
}
}
四、实践建议总结
事务设计原则:
- 能不用分布式事务就不用
- 优先考虑最终一致性
- 补偿机制必须完备
- 性能优化方向:
监控指标:
- 事务成功率/失败率
- 平均事务处理时间
- Saga/CQRS事件延迟
- 死锁发生率
通过合理运用DDD的事务边界设计和微服务事务模式,可以在保持系统松耦合的同时,满足业务数据一致性的要求。建议根据具体业务场景选择合适的事务策略,并在可靠性和性能之间取得平衡。