【问题标题】:DDD repository and factoryDDD 存储库和工厂
【发布时间】:2015-07-21 00:14:43
【问题描述】:

在我的应用程序中有几层。 本主题将重点介绍域和基础设施层。

我在域层中有存储库接口 ClientRepositoryInterface。 我在基础设施层实现了这个接口 ClientRepositoryImpl。

但是要在其存在周期的中间重构对象,我需要工厂(ReconstitutionClientFactory)。 调用工厂将在存储库中。 Eric Evans 的书被描述为一种正常的做法。

但是这个工厂(ReconstitutionClientFactory)应该在哪里呢?在域或基础设施层?

我认为在域中... 但!但是接下来下层会直接调用上层! 这是错误的,但如何做对呢?

【问题讨论】:

  • 在DDD中,“Repository”和“Factory”这两个概念都属于领域模型,因此属于领域层。他们可以使用来自基础设施的组件,但他们自己(接口实现)只是领域的一部分。基础架构必须不含特定于应用程序和领域的详细信息。

标签: domain-driven-design repository-pattern factory-pattern ddd-repositories


【解决方案1】:

工厂和存储库概念

为了回答您的问题,我认为重点关注 DDD 定义的概念的职责很重要。

在蓝皮书中,有一个部分处理您描述的问题:

工厂处理对象生命的开始;存储库有助于管理中间和结尾。

专门针对您的问题:

因为在这种情况下,REPOSITORY 是基于数据创建对象,所以许多人认为 REPOSITORY 是一个工厂——从技术角度来看确实如此。

(均引自 Evans,第 6 章,“与工厂的关系”一节)

为了保持概念的纯洁性,工厂和存储库的接口 干净是很重要的。所以不允许通过存储库接口创建新的业务对象,也不允许通过工厂接口查询现有的业务对象。

保持接口干净并不意味着您不应该使用存储库实现中的工厂,因为毕竟存储库会在某个时候创建​​一个实例,如果该实例创建是复杂,工厂是合适的解决方案。

再次引用埃文斯的话:

当从另一种介质重构对象时暴露出复杂性时,FACTORY 是一个不错的选择。

但是请注意,存储库很可能会调用工厂上的不同方法,而不是真正想要创建新域对象(而不是重构)的客户端。

在 Evans 的书中甚至有一个例子来说明方法:

回答您的问题

现在很明显这是允许的,让我们专注于您将工厂放在哪里的问题:

DDD 工厂接口属于域,因为您的域逻辑使用它来创建域对象。

DDD 重构工厂接口 不属于域,因为这仅与您的存储库相关。它在您的域的现实世界中不存在。

现在,如果您使用的架构禁止从域到基础架构的依赖关系(在应用 DDD 时您可能应该这样做),那么很明显工厂实现属于基础架构。请注意,无论您是否将图层称为层、环、领域或其他任何名称,依赖关系都是重要的部分。

【讨论】:

  • “DDD重构工厂接口不属于域”。当重构工厂返回一个域对象时,这怎么可能呢?您的两个陈述“禁止从域到基础设施的依赖关系”和“很明显工厂实现属于基础设施”似乎得出了与我完全相反的结论:) 这些难道不意味着它确实 not 属于基础设施?
  • @Matt 返回域对象并不是在域中放入某些东西的理由。重构工厂是一个纯粹的技术构造,领域专家不会知道它是什么以及为什么需要它。
  • @Matt:重构工厂仅由基础设施中的存储库使用。唯一需要从域访问的类似工厂的东西是(非重构)工厂接口。因此,该接口应该放在域中,而它的实现可以放在基础设施中。
  • 也许我读错了您的最后一条评论,但我不同意存储库存在于基础架构中。这个对话可能应该移到 StackOverflow cmets 之外;不过,我确实很欣赏您的见解-有趣的东西。感谢您抽出宝贵时间。
  • 客户端如何“查询”存储库?
【解决方案2】:

首先,分层方法有点过时了。当谈话层考虑“上下文”时,谁在谁之上并不重要。

存储库负责恢复一个对象。工厂只是创建一个新对象。注意不同的语义。存储库知道如何在持久性中保存/恢复/从持久性中完成,这取决于存储和访问方法。

所以,一切都在存储库中完成,即在基础设施中。如果您对事物进行序列化,那么您只需要反序列化即可(这就是文档数据库的工作方式)。如果您使用 ORM 或将内容存储在表中,那么您将执行获取数据和重新填充对象所需的所有查询。 ORM 是最简单的方法,因为它可以使用反射来填充私有属性。在这种情况下,ORM 本身就是工厂。

还有一件事,恢复,虽然在技术上可以由域工厂完成,但这不是工厂的目的,因为它打破了层边界。我们希望在基础架构中保留所有与持久性相关的内容。

【讨论】:

  • 难道你不能有一个知道如何从原始结果集中重构域对象的 SQLResultsetClientFactory 吗?并不是说这是一种常见的做法,但在这种情况下,它会存在于基础架构中,对吧?
  • MikeSW,非常感谢您的回复,但我的基本问题与 plalx 的问题相同。我想创建类似 SQLResultsetClientFactory 的东西。但它位于哪里?关于域......来自 DDD(Eric Evans) 的书:“使 REPOSITORY 将对象创建委托给 FACTORY,它(理论上,尽管在实践中很少)也可用于从头开始创建对象。”
  • @stalxed Eric 的书出版于 14 年前,请记住这一点。与持久性相关的所有内容都应保存在基础架构中。如果你真的想要一个 SqlResultsetClientFactory,那就去吧,但把它保留在基础设施中。
  • 首先,分层架构非常适合绝大多数业务应用程序,无论是现在还是一直——它并不过时;分层只是组织代码库的一种方式,一种简化编译时依赖图的方式。其次,存储库实现属于域,基础设施层。不要将分层的代码组织原则与促进实现“交换”的(通常是矫枉过正的)愿望混淆(在存储库的情况下,我们通过使用 ORM 库来实现这一点,而不是通过每个存储库的多个实现) .
  • @Rogério Repository 实现处理数据(持久性)访问。它是域的NEVER 部分。如果您将部分数据库从 rdbms 切换到文档或键/值,则 ORM 将毫无用处。
猜你喜欢
  • 2019-04-23
  • 2013-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多