如何正确创建通过ID引用另一聚合根的聚合根,规避并发无效引用问题?
并发场景下聚合根引用失效问题及解决方案
问题描述
存在两个聚合根A和B,A通过ID关联B。当执行以下并发操作时会出现数据不一致:
- 操作O1:创建新的A,先检查B是否存在
- 操作O2:并行删除B并完成
- 最终O1完成检查后保存A,导致A持有指向已删除B的无效引用
业务实例
课程(Lesson)与作业(Assignment)业务中,作业可关联课程或独立存在。创建关联课程的作业时,若课程被并行删除,会出现作业关联已失效课程的情况。
当前思路的困境
- 用Kafka等队列串行处理命令能避免问题,但不符合预期的架构设计;理想状态是服务监听删除事件的同时,通过API创建新聚合根
- 单体应用下仍存在极小窗口的并行执行可能,无法彻底规避
- 怀疑领域模型设计有问题,但未找到领域层的解决路径
可行解决方案
1. 领域层强化约束
- 事务内二次校验:单体应用中,将「检查课程存在+创建作业」放在同一个数据库事务中,保证原子性;微服务场景可采用分布式事务(如TCC),或让课程服务提供临时锁定接口——创建作业前先锁定目标课程,禁止删除,作业创建完成后再释放锁定。
- 调整领域规则:允许作业关联已删除课程,但标记为「关联失效」,后续通过异步任务清理无效关联,或在业务流程中提示用户修改关联对象。
2. 事件驱动的补偿机制
- 作业服务监听课程删除事件,收到事件后批量扫描关联该课程的作业,标记其关联状态为失效,或触发用户重新关联的业务流程。
- 优化作业创建流程:先将作业暂存为「待确认关联」状态,同时向课程服务发送有效性确认请求,根据返回结果将作业转为有效状态或直接告知创建失败。
3. 数据库层面防护
- 单体应用可利用外键约束:给作业表的
lesson_id字段设置外键,配置ON DELETE SET NULL(课程删除后作业关联字段置空,转为独立作业)或ON DELETE CASCADE(课程删除时同步删除关联作业,需符合业务逻辑)。 - 微服务场景可维护课程状态快照:创建作业时同时记录课程的版本号,保存作业时再次校验课程版本号是否一致,若不一致则说明课程已被修改/删除,拒绝创建请求;也可采用乐观锁机制实现类似效果。
内容的提问来源于stack exchange,提问作者Pedro
相关产品推荐
相关产品推荐

