【DDD】 领域模型应该依赖 repository 吗?

1,997 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天。

1.Repository

Repository 是《领域驱动建模》中提到的领域建模的building block 之一。Repository 封装了对数据库的访问,负责将领域对象持久化到数据库或者从数据库查询并重建领域对象。

2.领域模型与 repository 的关系

《领域驱动建模》书中第六章有个查询交互图,从这个交互图中我们可以看到 client(调用者)调用 repository 查封方法,repository 执行查询,并用查询到的数据重建了领域对象。 image.png

下图是第七章的一个类图,从图中我们可以看到 repository 依赖 entity(Cargo / Customer)。作者通过这些描述告诉我们 repository 依赖 Entity。 image.png

在目前比较流行的贫血模型中通常是由 service 层调用 repository 来存取 entity,只是这里的 entity 是只有属性,没有逻辑的数据对象而已。当我们尝试按照 DDD 来构建领域模型的时候,就很容易有这样的疑问,我们在写 entity 的业务逻辑的时候,经常需要引用其他的领域对象,如果没有 repository,这个领域对象哪里来呢?

所以就出现了 aggregate/entity 引用 repository 的情况。

3.领域模型不应该引用 repository,为什么

那么在 aggregate/entity 引用 repository 是不是好的实践呢?答案是否定的,原因如下:

3.1 与关注点分离单一职责SRP(Single Reposibility Principle)相悖

我们不应该在领域模型中引用 repository。业务逻辑和数据存取是两种不同的关注点。为了让设计简洁,减轻开发者的认知负担,我们应该一直提倡关注点分离,提倡 SRP。当我们将 repository 传递给 entity 的时候,就是把数据存取职责赋予了 entity,这就和我们提倡的理念相悖了。

3.3 增加了单元测试的难度

当我们的领域模型只依赖领域模型内部的对象的时候,也就是说我们的领域模型都是 POJO 的话,那么单元测试就会变得非常容易。但是当领域模型依赖 repository 的时候,如果之间使用数据库,那么我们的测试将会变得异常缓慢,这将慢慢消耗掉我们的耐心,我们将不会频繁的通过单元测试来验证我们的代码。当然我们可以用 mock 来替换数据库,但是这终究还是增加了单元测试的难度。

4.总结

所以通常的做法是:

  • service 层接收来自 UI/API/Controllers/Channels 等(面向外部)的调用
  • 通过 repository 从数据库加载领域对象(聚合或 entity),调用领域对象执行业务逻辑
  • 然后再次通过 repository 来业务逻辑的执行结果持久化到数据库。

5.参考