You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在DDD中正确实现Repository?关系型数据库适配难题求解

针对DDD聚合根持久化(从NoSQL到关系型数据库)的解决方案

核心原则:Repository封装持久化细节,领域层与存储实现解耦

不管底层是NoSQL单文档还是关系型分表,领域层和应用层都不应该感知存储方式的差异,所有适配逻辑都应该由Repository层承担。以下是具体思路:


1. 让聚合根追踪自身变更(推荐方案)

在Report聚合根内部维护内存级的变更集合,用于记录本次操作中新增/修改/删除的Transaction:

  • 在Report中添加private List<Transaction> addedTransactions;(仅内存存在,不持久化到数据库)
  • 当调用领域方法report.addTransaction(transaction)时,除了将交易加入主transactions集合,同时将其加入addedTransactions
  • Repository的save(Report report)方法内部逻辑:
    1. 先更新Report主表的信息(若有变更)
    2. 遍历addedTransactions,批量插入到Transactions表
    3. 清空addedTransactions,避免重复提交

这种方式下,应用层依然只需要调用reportRepository.save(report),领域层只负责业务规则校验(比如交易金额是否符合Report的限制),完全不需要关心底层是单文档还是分表存储。

2. 重新理解CRUD仓库的“更新”操作

CRUD仓库的update不等于“全量覆盖”,它的本质是持久化聚合根的当前状态。在关系型数据库下,Repository可以内部实现增量更新:

  • 当加载Report聚合时,同时加载关联的Transactions到内存
  • 保存时,对比内存中Transactions集合与数据库中的数据(或通过变更追踪标记),仅执行必要的插入/更新/删除操作
  • 所有对比、增量计算逻辑都封装在Repository内部,领域层和应用层完全无感知

3. 优化方案2:领域事件与持久化解耦

如果想用领域事件,注意不要让持久化逻辑依赖事件:

  • 领域层在完成addTransaction操作后,发布TransactionAdded事件,用于业务逻辑的延伸(比如触发报表计算、通知其他模块)
  • 持久化操作依然由Repository的save方法统一处理,事件只是业务协作的手段,不是驱动持久化的工具
  • 这种方式避免了因为存储变更而修改领域事件的定义,符合DDD关注点分离的原则

4. 聚合大小与CRUD仓库的关系

CRUD仓库并非仅适用于小型聚合,关键是聚合边界是否合理:

  • 只要Report作为聚合根的边界是正确的(比如Transaction的生命周期完全依赖Report,所有对Transaction的操作必须通过Report),无论聚合内包含多少实体,都可以用CRUD仓库
  • 你的问题本质不是聚合大小,而是Repository的实现没有封装好存储层的差异,导致上层逻辑被迫适配存储方式

内容的提问来源于stack exchange,提问作者Giorgos Ntymenos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 21:32:19