如何使用Spring Crud / Jpa存储库实现DDD



我想通过使用Spring实现DDD来创建一个应用程序。假设我有一个业务实体客户和一个接口客户存储库。

由于 spring 提供了CrudRepositoryJpaRepository来执行基本的 CRUD 操作和其他操作,例如默认情况下的查找器方法,我想使用它们。所以我的界面变成了

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long>{
}

但根据 DDD,接口应该在域层,实现应该在基础结构层。

现在我的问题是,客户存储库属于哪一层?

简短的回答:虽然它应该是域层中基础结构的任何依赖项,但为了 KISS,您可以这样做。如果你想成为 DDD 纯粹主义者,请在基础结构中定义一个CustomerRepository接口和一个实现这两个接口的实现。

冗长而无聊的答案:一般来说,域不应该关心或了解基础设施,因为它不应该依赖于其他层(基础设施、应用程序、表示或你正在使用的任何架构)。遵循此规则会导致更干净的体系结构。

特别是,域不应该关心持久性,它应该在内存中运行时运行。从域的角度来看,实体会变异,仅此而已,不需要持久性。

域代码的写入端实际上不需要持久性。当聚合执行命令时,它们已完全加载。执行命令后,聚合仅返回更改或新状态。聚合不会保留更改本身。它们是纯净的,没有可观察到的副作用。

我们作为架构师需要持久性,因为我们需要确保数据在重新启动之间持久化,并且我们可以同时在多台计算机上运行相同的代码。

然而,还有另一个需求是域代码,特别是域的读取和反应端(Sagas/进程管理器)。域的这些组件需要查询和筛选域实体。读取模型需要将实体返回给调用方,Sagas/进程管理器需要正确标识向其发送命令的正确聚合。

解决方案是仅在域层中定义接口,并在基础架构中进行实现。通过这种方式,域拥有接口,因此,根据依赖关系反转原则,它不依赖于基础结构。

在您的情况下,尽管域层依赖于 Spring 框架的基础架构部分的某些内容,但该部分只是一个接口。它仍然是JPA的一个缺点,因为你的域将使用它不拥有的方法,但在这种情况下KISS可能更重要。

另一种方法是定义一个接口,该接口不会使用实现此接口和 JpaRepository 接口的基础架构中的实现来扩展 JpaRepository。

哪种解决方案取决于您:更多的代码重复但更少的依赖性,或者更少的代码重复和更多的对 JPA 的依赖性。

最新更新