【问题标题】:How to implement DDD using Spring Crud/Jpa Repository如何使用 Spring Crud/Jpa Repository 实现 DDD
【发布时间】:2018-04-20 05:57:24
【问题描述】:

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

由于 spring 提供 CrudRepositoryJpaRepository 来执行基本的 CRUD 操作和其他操作,如默认的 finder 方法,我想使用它们。于是我的界面就变成了

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

}

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

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

【问题讨论】:

    标签: spring spring-data spring-data-jpa domain-driven-design ddd-repositories


    【解决方案1】:

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

    冗长而无聊的答案: 通常,域不应该关心或了解基础架构,例如,它不应该依赖于其他层(基础架构、应用程序、演示或任何您所在的架构)使用)。遵循此规则会导致更清洁的架构。

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

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

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

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

    解决方案是仅在域层中定义接口并在基础架构中实现。通过这种方式,域拥有接口,因此根据Dependency Inversion Principle,它不依赖于基础架构。

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

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

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

    【讨论】:

    • 感谢康斯坦丁加尔贝努的详细解释。据我了解,您提供了 2 个解决方案: 1. 将接口(IRepository)定义为域层中的标记接口,并与 JpaRepository 一起在基础设施层中实现它(公共接口 CustomerRepository 扩展 IRepository,JpaRepository)。 2.第二种解决方案是在基础设施层直接定义公共接口CustomerRepository extends JpaRepository 并在域中使用它(因为它只是一个接口)。我不明白 KISS 部分
    • @user1188867 ` 2. 第二种解决方案是直接在基础设施层定义公共接口CustomerRepository extends JpaRepository 并在域中使用它:几乎,你在域层定义它跨度>
    • 只是为了获得更多信息,我遇到了同样的问题。我已经做到了:领域层中的接口,完全符合您的需要。一个类在基础设施层实现接口。该实现“可操作”,使用 Spring 的 JPA 接口。通过这种方式,域存储库完全符合您的要求,并且可以在您需要测试的任何地方“存根”。另一方面,您仍然使用 JPA,因此您不会失去它的所有优势。
    • @LucaMasera 所以你的意思是域层中的 ICustomerRepository 和基础设施层中的 (CustomerRepository extends ICustomerRepository,JpaRepository ) 类,这类似于 Constantin Galbenu 建议的可能导致代码重复和没有办法强制开发人员实现 ICustomerRepository,因为我们正在扩展 ICustomerRepository 或者我缺少什么
    • 我已经这样做了: 1. 域中的接口ICustomerRepository,其名称与域语言更相关 2. 实现接口的类CustomerRepositoryAdapter,在基础设施层 3. JPA 接口ICustomerRepositoryJPA,它使用 JPA 来处理数据库。 CustomerRepositoryAdapter使用 JPA 接口工作。这是因为我使用 组合而不是继承 来完成工作。这样,如果需要,您可以拥有一个不同于数据库使用的 JPA 实体的域实体。
    猜你喜欢
    • 2016-01-12
    • 2020-08-28
    • 2015-09-24
    • 2015-03-15
    • 2018-11-13
    • 2017-11-23
    • 2020-10-30
    • 2018-01-09
    • 1970-01-01
    相关资源
    最近更新 更多