无法采用最终一致性时,如何解决Order与Warehouse限界上下文依赖?
我正在开发一个基于DDD(领域驱动设计)的订单系统,当无法采用最终一致性时,不确定如何实现Warehouse与Order两个限界上下文之间的通信。现有示例普遍推崇最终一致性(以亚马逊为例),但这类方案仅适用于无库存限制、可重新订购或按需生产的商品;对于活动门票这类限量商品(如单张座位票),最终一致性完全不适用——尤其是大量客户同时抢购同一张门票的场景。
如果将门票加入购物车与在Warehouse中锁定库存无法做到即时一致,会出现大量客户将同一张门票加入购物车,却在结账时发现门票已不可用的情况,这会严重影响用户体验;在门票即将售罄时,还会演变成不可接受的结账竞速问题。
因此必须确保选中的门票在加入购物车前被立即锁定/预留。为避免过期购物车长期锁定门票,可设置超时自动释放机制(如德国影院系统的20分钟购物车预留倒计时)。
我们的系统采用模块化单体架构并使用共享数据库,为此我列出了以下几种方案:
可选方案
1) 将Warehouse与Order合并为一个限界上下文
这种方案可实现事务级的即时一致性,但Warehouse与Order上下文有不同的业务需求,我更倾向于保持两者分离;此外,其他不限量商品无需即时一致性,合并会增加不必要的复杂度。
2) 跨两个上下文的数据库事务
由于当前系统使用共享数据库且两个上下文运行在同一进程中,可以打破DDD的建议,跨两个限界上下文开启数据库事务。这种方案会耦合上下文,但能直接解决即时一致性问题。
3) 使用直接调用而非集成消息
Warehouse上下文在应用层通过接口暴露预留命令,Order上下文依赖该接口并直接调用。仅当调用成功时,才将门票加入购物车。需要处理系统在两个操作之间崩溃的情况:重启后Order上下文需检查Warehouse中已预留但未加入购物车的门票,可通过商品状态标记限制未完成流程的重复调用。该方案耦合度较高,但实现简单,能解决问题。
4) 使用消息并等待响应
我对消息总线经验有限,不确定该方案的可行性。流程如下:
- 当Order上下文收到将门票加入购物车的请求时,发送
ItemRequested集成事件,然后在设定时间内等待Warehouse上下文的响应 - Warehouse上下文收到事件后,预留门票并发送
TicketReserved或TicketNotReserved集成事件 - 需要处理三种情况:
- 成功/失败消息在超时前到达:Order上下文正常处理
- 超时未收到消息:不将门票加入订单,Order上下文发送
TicketRejected事件 - Warehouse的消息到达过晚:Order上下文忽略该事件;若Warehouse收到
TicketRejected事件,则将门票重新放回可用库存
该方案保持了系统解耦,但复杂度更高,需要确保系统崩溃时消息不丢失。
疑问
针对模块化单体架构,还有其他可行方案吗?我认可方案4的解耦性,但为降低初期开发复杂度更倾向于方案3。是否有结构化的标准可以帮助选择合适的方案?
内容的提问来源于stack exchange,提问作者M. Koch

