仓储是什么
仓储是 DDD 中为聚合提供集合式访问的抽象:调用方按聚合根标识做获取、保存、删除,不感知底层是关系库、文档库还是内存实现。它的边界是聚合根而不是表,一个聚合根对应一个仓储,返回完整领域对象而不是行记录。接口留在领域层,实现放基础设施层,存储细节不渗入模型。
基本信息
| 职责 | 按标识获取、保存、删除聚合根 |
|---|---|
| 边界 | 一个聚合根对应一个仓储,不返回裸数据集 |
| 位置 | 接口在领域层,实现在基础设施层 |
| 事务 | 一次事务只变更一个聚合,由工作单元统一提交 |
| 标识 | 应用侧生成如 UUID,不依赖数据库自增 |
仓储与 DAO 的分工
| 维度 | 仓储 | DAO |
|---|---|---|
| 抽象层次 | 领域聚合的集合语义 | 表或记录的增删改查 |
| 返回对象 | 聚合根等完整领域对象 | 行记录或持久化实体 |
| 查询职责 | 按聚合标识存取,复杂查询外移 | 常承载各类条件查询方法 |
| 依赖方向 | 领域层依赖仓储接口 | 业务代码直接依赖数据访问实现 |
| 测试替换 | 可换成内存实现 | 通常需要真实数据库 |
最小示例
最小示例
order = orderRepository.findById(orderNo) # 按聚合标识整体加载
order.confirm(payment) # 领域行为在聚合内部完成
orderRepository.save(order) # 保存整个聚合,SQL 由实现决定
orderRepository.remove(orderNo) # 用领域语义命名,不叫 deleteById
# 复杂报表与多表联查不要塞进仓储,另建读模型或查询服务典型使用场景
- 领域层需要持久化对象但不愿依赖具体 ORM 时
- 读写分离:写走聚合仓储,读走独立查询服务
- 单元测试用内存实现替换数据库,领域逻辑测试不必起库
- 聚合边界评审:仓储方法若要跨聚合拼装,说明边界划错了
需要聚合的部分字段时先检查聚合边界是否被拆错:如果每次都要取一半数据,通常说明聚合划得过大或过小,而不是该给仓储加查询参数。