关于 ddd-repository
用于指导领域驱动设计中的 Repository。帮助开发者围绕完整聚合根定义领域语言操作,并将持久化细节隔离在领域层之外。
主要功能
- 01每个聚合根对应一个 Repository
- 02接口位于领域层
- 03实现在基础设施层
- 04使用领域语言命名查询
- 05说明读模型的边界
适用场景
- 01按标识加载或保存聚合
- 02替换暴露持久化类型的 DAO
- 03将展示查询移至读模型
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill ddd-repository安装后在 Claude Code 中输入/ddd-repository确认已加载。
详细介绍
一句话摘要
仓库将持久化隐藏在内存聚合集合的表象之下——领域代码无需知道存储使用的是 SQL、键值存储还是内存。
何时使用此技能
- 按标识加载或保存聚合。
- 替换那些将持久化类型泄漏到领域层的临时 DAO。
- 定义一小组具有领域意义的查询(
findActiveByCustomer,而非findByStatusEqualsAndCustomerIdEquals)。
何时不使用此技能
- 值对象——它们没有可用于查找的标识。
- 用于展示的跨聚合查询——应构建读取模型,而不是仓库方法。
- 报表 / 分析——这类需求需要针对读取优化的查询层,而不是领域仓库。
核心内容
仓库服务于一个聚合根。其接口位于领域层;其实现位于基础设施层。领域代码只依赖接口;基础设施代码负责接入实现。
接口使用通用语言。方法名称应描述业务要做什么,而不是持久化机制。findActiveByCustomer 是合适的。findByStatusEqualsAndCustomerIdEquals 则是披着领域外衣的 SQL 查询名称——抽象已经泄漏。
仓库处理的是完整聚合。它不会返回半个聚合、聚合内部的实体或数据库行。返回的是聚合根;调用方通过它进行操作。
如果查询用于展示——向用户显示订单列表、汇总客户活动、构建仪表板——通常就不属于仓库。应构建独立的读取模型,并提供自己的查询接口,针对视图进行优化。
决策启发
- 每个聚合根对应一个仓库,而不是每个实体对应一个仓库。
- 如果方法暴露了持久化类型(JPA 语境中的
Query、Predicate、Specification、EntityManager),说明抽象已经泄漏——应将这些类型下沉到更底层。 - 如果方法返回行、DTO 或部分实体,就不是领域仓库操作。应将它移到读取模型中。
- 如果仓库的方法超过约 5–8 个,应考虑其中是否有些其实是伪装成仓库方法的读取模型查询。
反模式
- 仓库返回行、DTO 或持久化类型。
- 每个实体各有一个仓库,从而暴露聚合内部结构。
- 使用随意的查找方法(
findByXAndYAndZAndW),而不是一小组命名清晰且具有领域意义的方法。 - 将框架的通用
JpaRepository/CrudRepository直接用作领域仓库——这会不加区分地暴露所有 CRUD 操作,并使领域层依赖 JPA。
另请参阅
ddd-aggregate——仓库操作的对象。ddd-factory——用于创建聚合;仓库负责处理其持久化生命周期。ddd-anti-patterns——会泄漏实现细节的仓库和以 CRUD 命名的方法。
参考资料
- Evans,《领域驱动设计》(2003),第 6 章(领域对象的生命周期)。
评论
支持 Markdown