需求分析与建模61 条

仓储(Repository)

仓储为聚合提供持久化的领域视角:按聚合根存取、隐藏存储细节,让领域层不依赖具体数据库与 ORM。

仓储是什么

仓储是 DDD 中为聚合提供集合式访问的抽象:调用方按聚合根标识做获取、保存、删除,不感知底层是关系库、文档库还是内存实现。它的边界是聚合根而不是表,一个聚合根对应一个仓储,返回完整领域对象而不是行记录。接口留在领域层,实现放基础设施层,存储细节不渗入模型。

基本信息

职责按标识获取、保存、删除聚合根
边界一个聚合根对应一个仓储,不返回裸数据集
位置接口在领域层,实现在基础设施层
事务一次事务只变更一个聚合,由工作单元统一提交
标识应用侧生成如 UUID,不依赖数据库自增

仓储与 DAO 的分工

维度仓储DAO
抽象层次领域聚合的集合语义表或记录的增删改查
返回对象聚合根等完整领域对象行记录或持久化实体
查询职责按聚合标识存取,复杂查询外移常承载各类条件查询方法
依赖方向领域层依赖仓储接口业务代码直接依赖数据访问实现
测试替换可换成内存实现通常需要真实数据库

最小示例

最小示例
order = orderRepository.findById(orderNo)   # 按聚合标识整体加载
order.confirm(payment)                       # 领域行为在聚合内部完成
orderRepository.save(order)                  # 保存整个聚合,SQL 由实现决定
orderRepository.remove(orderNo)              # 用领域语义命名,不叫 deleteById
# 复杂报表与多表联查不要塞进仓储,另建读模型或查询服务

典型使用场景

  • 领域层需要持久化对象但不愿依赖具体 ORM 时
  • 读写分离:写走聚合仓储,读走独立查询服务
  • 单元测试用内存实现替换数据库,领域逻辑测试不必起库
  • 聚合边界评审:仓储方法若要跨聚合拼装,说明边界划错了
需要聚合的部分字段时先检查聚合边界是否被拆错:如果每次都要取一半数据,通常说明聚合划得过大或过小,而不是该给仓储加查询参数。

易错点

  • 仓储接口堆满按条件查询的方法,退化成 SQL 包装器,领域语义丢失
  • 领域对象直接暴露 ORM 实体,持久化注解渗入模型层
  • 一个事务里保存多个聚合,一致性靠数据库事务兜底,扩容时立刻冲突
  • 用数据库自增主键充当聚合标识,测试与迁移都被数据库绑住

数据来源与参考

  • Eric Evans《Domain-Driven Design》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌