如何使用 Spring Crud/Jpa Repository 实现 DDD

use*_*867 4 spring domain-driven-design ddd-repositories spring-data spring-data-jpa

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

但是根据 DDD,接口应该在域层,实现应该在基础设施层。

现在我的问题是,CustomerRepository 属于哪一层?

Con*_*enu 8

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

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

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

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

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

然而,还有另一个需要域代码,特别是域的读取和响应端(Sagas/流程管理器)。域的这些组件需要查询和过滤域实体。Readmodels 需要将实体返回给调用者,而 Sagas/Process 管理器需要正确识别向谁发送命令的正确聚合。

解决方案是仅在域层中定义接口,并在基础设施中实现。通过这种方式,域拥有接口,因此,根据依赖倒置原则,它不依赖于基础设施。

在你的情况下,虽然领域层取决于一些Spring框架的基础设施的一部分,这东西只是一个接口。它仍然依赖于 JPA,因为您的域将使用它不拥有的方法,但在这种情况下 KISS 可能更重要。

另一种方法是定义一个不扩展 JpaRepository 的接口,并在实现此接口和 JpaRepository 接口的基础结构中实现。

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

  • 我已经这样做了: 1. 域中的接口`ICustomerRepository`,具有一些名称与域语言更相关的函数 2. 一个实现接口的类`CustomerRepositoryAdapter`,在基础设施层 3. JPA 接口, `ICustomerRepositoryJPA`,它使用 JPA 来处理数据库。类`CustomerRepositoryAdapter` _使用_ JPA 接口工作。这是因为我使用了 _composition 而不是继承_来完成工作。这样,如果需要,您可以拥有一个与数据库使用的 JPA 实体不同的域实体。 (3认同)