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

4

我希望使用Spring实现DDD来创建一个应用程序。假设我有一个业务实体“客户”和一个接口“CustomerRepository”。

由于Spring提供了CrudRepositoryJpaRepository来执行基本的CRUD操作以及其他操作,例如查找方法,我想要使用它们。因此,我的接口变成:

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

}

根据领域驱动设计(DDD)的理念,接口应该在领域层中,而实现应该在基础设施层中。那么我的问题是,CustomerRepository属于哪一层?
1个回答

8
简短回答:尽管领域层不应该有依赖基础设施,但为了KISS,您可以这样做。如果您想成为DDD纯粹主义者,则需要在基础设施中定义一个CustomerRepository接口和实现,该实现同时实现这两个接口。 冗长而乏味的回答:一般来说,领域层不应关心或知道基础设施,也就是说,它不应该依赖于其他层(基础设施、应用程序、演示或任何您正在使用的架构)。遵循这个规则会导致更清晰的架构。
特别地,领域层不应关心持久性,它应该表现得像在内存中运行一样。从领域的角度来看,实体发生变化就可以了,不需要持久性。
领域代码的写入端实际上不需要持久性。当聚合执行命令时,它们已经完全加载。命令执行后,聚合只返回更改或新状态。聚合本身不保存更改。它们是纯净的,没有可观察的副作用。
我们作为架构师需要坚持不懈,因为我们需要确保数据在重启后持久存在,并且可以在同一时间在多台机器上运行相同的代码。
然而,域代码,特别是域的读取和反应部分(Sagas/Process managers),还有另一个需求。这些域的组件需要查询和过滤域实体。读模型需要将实体返回给调用者,而Sagas/Process managers需要正确地识别要发送命令的正确聚合。
解决方案是仅在域层中定义接口,并在基础设施中进行实现。通过这种方式,域拥有接口,因此根据依赖倒置原则,它不依赖于基础设施。
在您的情况下,尽管域层依赖于Spring Framework的基础设施部分的某些内容,但那些内容只是接口。它仍然是对JPA的依赖,因为您的域将使用它不拥有的方法,但在这种情况下,KISS可能更重要。
另一种选择是定义一个不扩展JpaRepository的接口,并在基础设施中实现此接口和JpaRepository接口的实现。哪种解决方案取决于您:更多的代码重复但依赖性较少,或更少的代码重复和更多对JPA的依赖。

感谢详细的解释,Constantin Galbenu。我理解您提供了两个解决方案:1. 在领域层中将接口(IRepository)定义为标记接口,并在基础设施层中实现它,以及JpaRepository(public interface CustomerRepository extends IRepository,JpaRepository <Customer,Long>)。2. 第二种解决方案是直接在基础设施层中定义公共接口CustomerRepository extends JpaRepository <Customer,Long>,并在领域中使用它(因为它只是一个接口)。我没有理解那个KISS部分。 - user1188867
1
仅供参考,我曾经遇到过同样的问题。我做了这个:在领域层中创建接口,就像你需要的那样。一个类在基础设施层中实现该接口。为了使实现“有效”,它使用Spring的JPA接口。通过这种方式,领域存储库正是您想要的,可以在测试时随处“存根”。另一方面,您仍然使用JPA,因此不会失去所有优势。 - Luca Masera
3
我已经完成了以下工作:
  1. 在领域层中创建了一个名字与领域语言更相关的接口ICustomerRepository,包含一些函数。
  2. 在基础设施层中创建了一个实现该接口的类CustomerRepositoryAdapter
  3. 创建了JPA接口ICustomerRepositoryJPA,用于与数据库交互。
CustomerRepositoryAdapter使用JPA接口来工作。这是因为我使用了“组合优于继承”来完成工作。这种方式可以使领域实体与数据库使用的JPA实体不同,以满足需要。
- Luca Masera
你好,能否有人提供一个实现的例子呢?这样会更容易理解,而不是写一大段文字。我们更懂得代码而非文字。谢谢。 - James
@ConstantinGalbenu 正在使用该存储库的代码不仅依赖于Spring存储库接口,还依赖于客户端,即DAO而不是实体域。 因此,使用Spring接口两次违反了域依赖规则。更加DDD纯粹主义的解决方案是在域中定义存储库接口,在基础设施中实现将域模型转换为Spring DAO,然后使用Spring存储库最终存储域实体。 - Canemacchina
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接