DDD与CQRS实践:单个命令处理程序中能否使用多个仓储?
跨聚合「校验预留」类场景实现方案参考
典型场景示例
用户将商品添加到购物车后点击「Checkout」,触发「Create Order」命令。在数据库中创建状态为「Payment Expected」的订单记录及对应订单项前,需校验用户所选商品仍有库存(可能用户加购时还有库存,当前已售罄),同时需要预留对应库存,避免用户结账过程中库存被其他用户占用。
已梳理的可选实现方案
- 在「Create Order」命令处理程序中,直接调用
ProductStockRepository完成库存预留,操作成功后再调用OrderRepository创建订单,即单个处理程序中使用多个仓储。 - 不在「Create Order」处理程序中直接调用
ProductStockRepository,而是封装ProductStockService,通过调用该服务的方法完成库存校验与预留。本质还是单个处理程序中使用多个仓储,只是对库存仓储的调用做了抽象。 - 定义内部「Reserve Products」命令,在「Create Order」命令处理程序内部派发并等待该命令执行完成。
- 用户点击「Checkout」按钮时先触发「Reserve Products」命令而非「Create Order」命令,在「Reserve Products」处理程序中尝试预留库存,操作成功后发布「Products Reserved」领域事件,对应的事件处理程序监听到该事件后再创建订单。
- 其他可扩展方案。
行业通用标准实现方案
这类跨多个业务操作的一致性场景,没有绝对最优解,需要根据系统架构、并发量级、一致性要求选择对应方案:
低并发单体系统:本地事务包裹方案
如果是单体应用、并发量级不高,直接用本地数据库事务包裹全流程即可,不管是直接调用多仓储还是通过领域服务封装操作,只要库存预留和订单创建在同一个事务内提交,就能保证原子性,实现成本最低,是中小系统的首选方案。
高并发/微服务系统:事件驱动最终一致性方案
如果库存和订单属于不同微服务,或者需要支撑高并发流量,标准实现为第四种事件驱动方案。需要配套两个补偿机制避免数据不一致:
- 给库存预留添加过期自动释放规则,比如预留15分钟未完成订单创建则自动回滚库存,避免无效占用
- 消息投递加ACK确认、消费端加幂等处理,保证领域事件不会丢失、不会被重复消费
强一致要求分布式系统:TCC分布式事务方案
如果业务不允许短时间的数据不一致,采用TCC(Try-Confirm-Cancel)分布式事务实现:
- Try阶段:分别执行库存预留、订单预创建操作
- Confirm阶段:两个Try操作都成功后,正式生效订单、确认库存预留
- Cancel阶段:任意一个Try操作失败,回滚已预留的库存、删除预创建的订单
内容的提问来源于stack exchange,提问作者CKK
相关产品推荐
相关产品推荐

