【问题标题】:Some confusion with dependencies between Application and Infrastructure layers应用程序和基础设施层之间的依赖关系有些混乱
【发布时间】:2013-01-13 10:06:17
【问题描述】:
  1. 当应用程序使用 Repository(这是一个 Infrastructure 服务)时,Repository 接口 定义在 Domain 中层,而它的实现是在基础设施层中定义的,因为这样域模型没有传出依赖项。

a) 如果我们 100% 确定特定(非存储库)基础设施服务 InfServ 将永远不会被 中的任何代码调用领域层,那么我假设InfServ的接口不需要在领域层中定义?

  1. 假设 InfServ 不会被 Domain 层中的代码调用(因此我们不需要在 Domain 层 中定义它的接口):

a) 如果 应用层的服务 将调用 InfServ,我假设 应用层 应该对 Infrastructure 有隐式依赖层 而不是相反?换句话说,InfServ 的接口它的实现都应该在基础设施层中定义?

b)应用层最好依赖基础设施层而不是其他方式的原因是基础设施层 em> 可以被许多应用程序重用,而 应用层 在大多数情况下仅特定于单个应用程序,通常不能被其他应用程序重用?

c) 如果我们 100 % 确定 Domain 层 中的任何代码都不会使用 Repository,那么我们就不需要在 中定义它的接口>域层

更新:

1)

是的,但是领域层的定义可以包括应用 充当域外立面的服务。应用程序 服务通常引用存储库和其他基础设施 服务。它编排域对象和所述服务以实现 用例。

一)

...充当域的外观

难道我们不能争辩说“常规”(我在我的问题中提到的那些)应用程序服务也可以作为域的门面吗?

b)

应用程序服务通常引用存储库和其他 基础设施服务。

从您的回复看来,您似乎在暗示(尽管您可能不是)“常规”应用程序服务通常不引用基础设施服务 ,实际上他们是这样做的(据我所知)?!

2)

一)

如上所述,我通常将应用程序服务合并到 领域层。

“常规”应用层也是 BLL 层的一部分,所以我们不能说它与 域层 合并(它实际上位于域层 )?!

b)

我倾向于坚持六边形建筑风格...

看起来六边形架构没有明确的应用层概念(即应用服务)?

c)

在域中声明存储库接口的部分好处 层是它指定了域的数据访问要求。

所以我们应该在 Domain 层 中包含 Repository 接口,即使我们的域代码不会使用它?

第二次更新:

2a)

如果您所谓的“常规”应用程序服务与 域,我认为将其作为域的一部分是可以接受的 “层”。但是,域不应直接依赖于 围绕应用程序服务,因此可以将它们拆分 如果需要,可以分成单独的模块。

  • 我不确定您是否暗示 应用层(在传统的分层架构中)的设计与合并时有任何不同 应用层 域层使用例如洋葱架构

  • 我想说,至少就模块而言,两者应该没有什么不同,因为在这两种情况下,我们都可以将 ApplicationDomain 层 模块分开? (尽管我必须承认我跳过了伴侣模块(Evan 的书),因为我认为在学习 DDD 时我不需要这些知识:O)

2b)

是的,因为它可以与分层架构进行对比。一个严格的 分层架构不符合声明的想法 领域层中的存储库接口并在 基础设施层。这是因为,一方面,你有一个 在一个方向上的依赖,但在部署方面依赖 在另一个。 Hexagonal 通过放置 域为中心。看看洋葱架构 - 它是 基本上是六边形,但可能更容易掌握。

我还不知道 MVC 模式或 Asp.Net MVC,但无论如何,从阅读该系列的前三部分(这让我感到困惑到我停止阅读它),它似乎:

a) 来自Onion article

每一层都与它下面的层耦合,而且每一层往往是 再加上各种基础设施问题。

作者暗示在传统的分层架构 TLA 中,域层基础设施层耦合,这当然不是真的,因为我们通常定义 域层中的基础设施接口(例如Repository接口)?!

b) 如果在使用 TLA 时我们决定在 Application layer 中定义 Infrastructure interfaces,那么 Application layer 也不会耦合到基础设施层?!

c) Onion 架构 不是*基础设施层* 耦合到应用程序核心(包括 应用程序层),因为 基础设施接口Application Core中定义?

d) 如果 c) 是,那么将 Application 层 耦合到 Infrastructure 层 不是更好吗(出于我让步的原因我最初的问题(这里我假设域层不会调用基础设施服务)?

4)

来自Onion article

围绕领域模型的第一层通常是我们需要的地方 找到提供对象保存和检索行为的接口, 称为存储库接口。

来自Onion article

控制器只依赖于接口,接口定义在 应用核心。请记住,所有依赖项都指向 中心。

作者似乎在暗示,由于依赖关系只是内部的,并且基础设施接口是围绕域模型定义的,因此域模型中的代码不应引用这些接口。换句话说,我们不应该将存储库引用作为参数传递给域实体的方法(正如您自己所说的那样):

class Foo
{
           ...
       public int DoSomething(IRepository repo)
       {
              ...
            var info = repo.Get...;
              ...
       }
}

出于上述原因,我必须承认,我看不到使用 Onion 架构 的好处,甚至看不到它与 TLA 有何不同(假设所有 Infrastructure 接口 都已定义域层 ) --> 换句话说,我们不能用洋葱架构图来描述TLA吗?!

最终更新:

2)

b)

是的。在 TLA 中,领域层将直接与 基础设施层声明的类的基础设施 反之亦然。

为了确定——您是说使用 TLA,Infrastructure 接口 将在 Infrastructure 层 中定义(我知道使用 Hexagonal/Onion 架构定义了它们在应用程序核心)?

d)

耦合是双向的。应用核心依赖于 基础设施和基础设施中的实现取决于 在应用程序核心中声明的接口。

我的观点是,由于在 Onion 架构中,InfServ 接口 是在 应用层 中声明的(这里的假设是 InfServ 永远不会被 Domain Layer 调用,因此我们决定不在 Domain Layer 内定义 InfServ 接口 >——见原1a问题),表示应用层控制InfServ接口。但我认为如果 Infrastructure 层 控制 InfServ 接口 会更好,因为原始 2b 中所述的原因 问题?!

4)

看来作者的意思是因为依赖关系只是向内的 由于基础设施接口是围绕领域模型定义的, 领域模型中的代码不应引用这些接口。

在我看来,您的代码示例适合代码。

所以我说洋葱架构不“允许”域模型引用基础设施接口是正确的,因为它们是在InDe(InDe 当然也存在于应用程序核心中)围绕 DM strong text并从 DM 引用它们是否意味着依赖关系从 DM 上升到 InDe

DDD 通常以六角形/洋葱形建筑风格呈现 这就是为什么可能会有一些混乱。我想你可能已经 一直在做的已经是六边形了,这就是为什么它看起来像 同样的事情。

是的,我也有这种印象。虽然我确实计划在阅读完 Evan 的书后更深入地研究 Hexagonal 架构(特别是因为新的 DDD 书将基于它的一些示例)。

第四次更新:

2)

d)

如果基础设施拥有接口和实现,那么域 或应用层将负责实现持久性 为自己。

我假设 ApplicationDomain 层需要自己实现它,因为引用 Infrastructure 层 中定义的接口将遵循 Onion 的内层规则不依赖外层(基础设施层是外层)?

4)

域模型可以引用基础设施接口,例如 存储库,因为它们是一起声明的。如果应用层 从域中分离出来,就像在洋葱图中一样,然后是域层 可以避免引用接口,因为它们可以在 应用层。

但根据那篇文章,基础设施接口是在域层周围的层中声明的,这意味着它更接近应用程序核心的外边缘 而不是 Domain Layer – 正如文章所指出的,内层不应该依赖外层>!

谢谢

【问题讨论】:

    标签: domain-driven-design


    【解决方案1】:

    1a) 是的,但是域层的定义可以包括在域上充当facade 的应用程序服务。应用程序服务通常引用存储库和其他基础设施服务。它协调域对象和所述服务以实现用例。

    2a,b)

    如上所述,我通常将应用程序服务合并到域层中。我倾向于坚持Hexagonal architecture 风格,也称为端口和适配器。域位于中心,由应用程序服务封装,所有外部连接,包括存储库、UI 和服务都充当端口。

    2c) 在域层声明存储库接口的部分好处是它指定了域的数据访问要求。

    更新

    1a) 我不打算将我提到的应用程序服务与“常规”应用程序服务区分开来。如果它是域上的外观,则适用规则。

    1b) 可能有一些服务仍然可以称为应用程序服务,但与域没有直接关系,我想排除这些服务。

    2a) 如果您所谓的“常规”应用程序服务与域交互,我认为将其作为域“层”的一部分是可以接受的。但是,域不应直接依赖于周围的应用程序服务,因此如果需要,可以将它们拆分为单独的模块。

    2b) 是的,因为它可以与分层架构进行对比。严格的分层架构不符合在领域层声明存储库接口并在基础设施层实现的想法。这是因为一方面您在一个方向上存在依赖关系,但在部署方面,依赖关系在另一个方向上。 Hexagonal 通过将域置于中心来解决这些问题。看看onion architecture - 它本质上是六边形的,但可能更容易掌握。

    2c) 是的,但通常应用程序服务会引用存储库接口。

    更新 2

    2a) 它们的实施方式可能不同,但一般职责是相同的。主要区别在于依赖关系图。虽然您可以使用任一架构将应用程序服务分离到它们自己的模块中,但洋葱/六边形架构强调使用接口来声明对基础架构的依赖关系。

    2ba) 是的,这实际上是洋葱架构的一个特征。

    b) 是的。在 TLA 中,域层将根据基础设施层声明的类直接与基础设施通信,而不是相反。

    c) 是的,基础设施实现了领域层声明的接口。

    d) 耦合是双向的。应用核心依赖于基础设施中的实现,而基础设施依赖于应用核心中声明的接口。

    4) 在我看来,您的代码示例是合适的代码。在某些情况下,实体需要访问存储库才能执行某些操作。但是,为了改善此代码的耦合特性,最好定义一个声明实体所需功能的特定接口,或者如果 lambdas 更好可用。然后存储库可以实现该接口,并且应用程序服务将在调用给定行为时将存储库传递给实体。这样,实体不依赖于通用的存储库接口,而是依赖于非常特定的角色。

    DDD 通常以六角形/洋葱形架构风格呈现,这就是为什么可能会有些混乱。我认为您可能一直在做的已经是六边形了,这就是为什么它看起来是一样的。

    更新 3

    2b) 在 TLA 中不会有接口,或者不是同一种接口。域将直接与基础架构(例如持久性框架)进行通信,因此它将负责持久性。

    2d) 如果基础设施拥有接口和实现,那么域或应用层将负责自己实现持久性。在 hexagonal/onion 中,持久性的实现是基础架构的一部分——它将抽象域“适应”到数据库。

    4) 域模型可以引用基础设施接口,例如存储库,因为它们是一起声明的。如果应用层从域中分离出来,就像洋葱图那样,那么域层可以避免引用接口,因为它们可以在应用层中定义。

    更新 4

    2d) 该语句不适用于具有如下层次结构的分层架构:UI -> 业务逻辑 -> 数据访问。业务逻辑层依赖于数据访问层,必须基于数据访问框架的对象模型来实现其数据访问。数据访问框架本身对业务层一无所知。

    4) 文章仅指定了一种可能的架构。有可接受的变化。

    【讨论】:

    • 我做了另一个更新,但无论如何我都会标记你的帖子,因为你已经提供了足够多的帮助
    • 一如既往的好答案。谢谢
    【解决方案2】:

    我看到将存储库接口放置在应用程序层而不是域中的唯一好处是,如果您想在另一个应用程序中重用域 DLL,您将完全重新定义查询域实体集合的方式 - 换句话说,需要完全不同的存储库。

    但是,这种方法有两个主要障碍:

    • 域层服务。它们代表处于许多实体交叉点的流程,因此需要多个引用来完成它们的工作。对他们来说,通过询问 Repositories 来获取这些引用是一件常见且方便的事情。

      您可以在 RoutingService here 中找到这方面的示例。

    • 此外,在某些特殊情况下,某些实体可能需要存储库的帮助才能找到特定的其他实体。例如,如果您有一个聚合根的层次结构,它们之间具有父/子关系,一个特定的实例可能会说“我需要满足这些条件的所有祖先/后代的列表”。存储库似乎最适合这种查询。

    【讨论】:

    • A - 那么您对问题 1a、2a 和 2b 有何看法? B - 假设您编写了 RoutingService 示例,如果不是将 Repository 传递给 RoutingService 的构造函数,而是将它作为参数传递给那些需要它的 RoutingService 方法,那不是更好吗?!
    • 如果您的意思是传递所需的实体,我认为这不会更好,因为这基本上意味着将算法细节从服务转移到其消费者,如果有多个消费者,可能会出现代码重复.例如,IList<Voyage> voyages = _voyageRepository.FindBeginingBefore(routeSpecification.ArrivalDeadline) 是路由过程的详细信息,它在 RoutingService 中而不是在其使用者中。
    • 如果您的意思是将存储库传递给方法而不是通过构造函数注入它,那么这没什么区别,并且无论如何都要求域服务具有对存储库接口的引用。
    • 所以你不同意eulerfx的基础设施层应该有一个依赖应用层?
    • 我认为他指的是应用程序核心而不是应用程序层,这是不同的。 Application Core 也封装了领域层。由于我建议基础设施层引用域层,我想我们有点在同一页面上;)
    猜你喜欢
    • 2013-09-19
    • 2019-01-27
    • 2017-06-10
    • 2021-12-25
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    相关资源
    最近更新 更多