13
  1. 当应用程序使用存储库(这是一个基础设施服务)时,存储库接口被定义在域层中,而它的实现被定义在基础设施层中,因为这样域模型没有外向依赖关系。

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

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

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

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

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

更新:

1)

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

一种)

...充当域的立面

难道我们不能争辩说“常规”(我在我的问题中提到的那些)应用程序服务也充当域的外观吗?

b)

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

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

2)

一种)

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

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

b)

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

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

C)

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

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

第二次更新:

2a)

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

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

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

2b)

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

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

a)来自洋葱文章

每一层都耦合到它下面的层,并且每一层通常耦合到各种基础设施问题。

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

b) 如果在使用 TLA 时我们决定在应用层定义基础设施接口,那么应用层也不与基础设施层耦合?!

c)由于基础设施接口是在应用程序核心中定义的,因此洋葱架构不是与应用程序核心(包括应用程序层)耦合的*基础设施层*吗?

d)如果c)是,将应用程序层耦合到基础设施层不是更好吗(出于我在原始问题中给出的原因(这里我假设域层不会调用基础设施服务))?

4)

来自洋葱文章

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

来自洋葱文章

控制器仅依赖于在应用程序核心中定义的接口。请记住,所有依赖项都朝向中心。

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

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

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

最后更新:

2)

b)

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

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

d)

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

我的观点是,由于 Onion 架构在应用层中声明了InfServ接口(这里假设InfServ从未被域层调用,因此我们决定不在域层中定义InfServ接口——参见原始1a问题),它表示应用层控制InfServ接口。但我认为如果基础设施层控制InfServ接口会更好,因为原始2b中所述的原因问题?!

4)

看来作者是在暗示,由于依赖关系只是内部的,而且基础设施接口是围绕领域模型定义的,领域模型中的代码不应该引用这些接口。

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

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

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

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

第四次更新:

2)

d)

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

我假设应用程序层需要自己实现它,因为引用基础设施层中定义的接口将洋葱的内层规则不依赖于外层(基础设施层是外层)?

4)

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

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

谢谢你

4

2 回答 2

2

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

2a,b)

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

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

更新

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

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

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

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

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

更新 2

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

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

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

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

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

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

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

更新 3

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

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

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

更新 4

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

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

于 2013-01-29T18:53:34.340 回答
1

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

然而,这种方法有两个主要障碍:

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

    RoutingService 您可以在此处找到一个示例。

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

于 2013-01-30T14:58:46.500 回答