对 Spring-Data DDD 存储库模式感到困惑

Posted

技术标签:

【中文标题】对 Spring-Data DDD 存储库模式感到困惑【英文标题】:Confused about Spring-Data DDD repository pattern 【发布时间】:2011-08-23 22:18:43 【问题描述】:

我不太了解 DDD 存储库模式,但 Spring 中的实现让我感到困惑。

public interface PersonRepository extends JpaRepository<Person, Long>  … 

由于接口扩展了 JpaRepository(或 MongoDBRepository...),如果您从一个 db 更改为另一个,您也必须更改接口。

对我来说,接口是用来提供一些抽象的,但在这里它不是那么抽象......

你知道为什么 Spring-Data 会这样工作吗?

【问题讨论】:

【参考方案1】:

在 Spring Data 的 M2 之前,我们要求用户扩展 JpaRepository,原因如下:

    类路径扫描基础架构只选择扩展该接口的接口,因为可能会并行使用 Spring Data JPA 和 Spring Data Mongo,并且它们都指向同一个包,不清楚要为哪个存储创建代理.然而,自从 RC1 以来,我们只是将这个负担留给了开发人员,因为我们认为这是一个相当奇特的案例,并且仅使用 RepositoryCrudRepository 或类似的好处超过了您在刚刚描述的极端案例中所付出的努力。您可以在命名空间中使用 excludeinclude 元素来对此进行更细粒度的控制。 在 M2 之前,我们通过重新声明 CRUD 方法并用 @Transactional 注释它们来将事务性应用于 CRUD 方法。这一决定又是由AnnotationTransactionAttributeSource 用于查找事务配置的算法驱动的。因为我们希望通过在具体存储库接口中重新声明 CRUD 方法并在其上应用 @Transactional 来为用户提供重新配置事务的可能性。对于 RC1,我们决定实现自定义 TransactionAttributeSource,以便能够将注释移回存储库 CRUD 实现。

长话短说,归结为:

从 RC1 开始,不再需要扩展商店特定的存储库接口,除了您想要...

    在更核心的存储库接口中使用基于List 的访问findAll(…) 而不是基于Iterable 的访问(尽管您可以简单地在公共基础接口中重新声明相关方法以返回Lists好) 您想使用 JPA 特定的方法,例如 saveAndFlush(…) 等。

一般来说,自 RC1 以来,您在 CRUD 方法的公开方面更加灵活,因为您甚至可以仅扩展 Repository 标记接口并有选择地添加您想要公开的 CRUD 方法。由于支持实现仍将实现PagingAndSortingRepository 的所有方法,我们仍然可以将调用路由到实例:

public interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> 

  List<T> findAll();

  T findOne(ID id);


public interface UserRepository extends MyBaseRepository<User, Long> 

  List<T> findByUsername(String username);

在该示例中,我们定义 MyBaseRepository 以仅公开 findAll()findOne(…)(将被路由到实现 CRUD 方法的实例中)并且具体存储库向两个 CRUD 方法添加一个查找器方法。

有关该主题的更多详细信息,请咨询reference documentation。

【讨论】:

【参考方案2】:

这背后的原因在这篇博文http://blog.springsource.com/2011/02/10/getting-started-with-spring-data-jpa/ 中已经很清楚地说明了。

定义这个接口有两个目的:首先,通过扩展 JpaRepository,我们在我们的类型中获得了一堆通用的 CRUD 方法,这些方法允许保存帐户,删除它们等等。其次,这将允许 Spring Data JPA 存储库基础架构扫描此接口的类路径并为其创建一个 Spring bean。

如果您不信任与来源如此接近的来源(双关语),那么阅读这篇文章可能是个好主意http://www.brucephillips.name/blog/index.cfm/2011/3/25/Using-Spring-Data-JPA-To-Reduced-Data-Access-Coding。

我不需要编写代码的是 PersonRepository 接口的实现。 Spring 将创建此接口的实现,并使 PersonRepository bean 可用于自动装配到我的 Service 类中。 PersonRepository bean 将具有所有标准 CRUD 方法(将是事务性的)并返回 Person 对象或 Person 对象的集合。因此,通过使用 Spring Data JPA,我节省了编写自己的实现类。

【讨论】:

我不关心实施。如果只需要扫描存储库,为什么不扩展一个更通用的存储库接口呢?他们谈论保存和删除,我对 mongoDB 不太了解,但我想我们也可以在 mongoDB 上执行这些操作...如果您将诸如刷新之类的方法公开给您的服务,您会使它们依赖于 orm 吗?【参考方案3】:

你是对的,从外部的角度来看,接口是对所有实现类都有效的东西的抽象。

这正是这里发生的事情:

JpaRepository 是所有 JPA 存储库(针对所有不同实体)的通用视图,而 MongoDBRepository 对所有 MongoDB 实体都是相同的。

但是 JpaRepository 和 MongoDBRepository 没有任何共同之处,除了在公共超级接口中定义的东西:

org.springframework.data.repository.PagingAndSortingRepository org.springframework.data.repository.Repository

所以对我来说它看起来很正常。

如果您使用实现 Repository 的类,那么如果您希望能够从 JPA 实现切换到基于 Document 的实现,请使用 PagingAndSortingRepository 或 Repository(抱歉,我无法想象这样的用例 - 无论如何)。当然,您的 Repository 实现应该实现正确的接口(JpaRepository、MongoDBRepository),具体取决于它是什么。

【讨论】:

实际上,如果您在接口中知道 Repository 实现是给定类型的,那为什么还要使用接口呢?但我想像你一样很难做得更好...... 我认为/期望从 RDBMS 到面向文档的存储的更改需要对对象/域模型进行重大更改。无论如何,持久性抽象会以一种或另一种方式泄漏。总是。如果可以简单地从 JpaRepository 更改为 MongoDBRepository 而无需对域模型进行重大更改,我猜想域模型一开始并没有那么复杂。这没什么错,因为某些应用程序就是这样。

以上是关于对 Spring-Data DDD 存储库模式感到困惑的主要内容,如果未能解决你的问题,请参考以下文章

Dapper 的 DDD 原则和存储库

存储库模式 - 如何正确处理 JOIN 和复杂查询?

DDD Repository:使用DAO进行分离?

存储库模式:如何延迟加载?或者,我应该拆分这个聚合吗?

.NET 的 SOLID DDD ORM 请求(使用干净的实体和存储库)

规范模式和 DDD