use*_*291 11 domain-driven-design
1)当应用程序使用存储库(基础结构服务)时,则存储库接口在域层中定义,而其实现在基础架构层中定义,因为域模型没有传出依赖关系.
a)如果我们100%确定域层内的任何代码都不会调用特定的(非存储库)基础设施服务 InfServ,那么我认为不需要在域层内定义InfServ的接口?
2)假设域层内的代码不会调用InfServ(因此我们不需要在Domain层中定义其接口):
a)如果应用层的服务将调用InfServ,我假设应用层应该对Infrastructure层有一个隐含的依赖,而不是相反?换句话说,无论InfServ的接口和它的实现应在中定义的基础设施层?
b)为什么Application层更好地依赖于Infrastructure层而不是其他方式,这是因为Infrastructure层可以被许多应用程序重用,而Application层在大多数情况下只针对单个应用程序而且通常其他应用程序无法重用?
c)如果我们100%确定Domain层中的代码不会使用Repository,那么我们不需要在Domain层中定义它的接口吗?
更新:
1)
是的,但是域层的定义可以包括充当域的外观的应用程序服务.应用程序服务通常引用存储库和其他基础结构服务.它协调域对象和所述服务以实现用例.
一个)
......充当领域的门面
难道我们不能说"常规"(我在问题中提到的那些)应用服务也可以作为域名的外观吗?
b)
应用程序服务通常引用存储库和其他基础结构服务.
从您的回复中看起来好像您建议(虽然您可能不是)"常规" 应用服务通常不会引用基础设施服务,实际上他们这样做(据我所知)?!
2)
一个)
我通常将应用程序服务(如上所述)合并到域层中.
"常规" 应用程序层也是BLL层的一部分,所以我们不能认为它与Domain层合并(它实际上位于Domain层之上)?!
b)
我倾向于坚持六角形建筑风格......
看起来六边形架构没有应用层(即应用服务)的明确概念?
C)
在域层中声明存储库接口的部分好处是它指定了域的数据访问要求.
所以我们应该在域层中包含Repository接口,即使我们的域代码不会使用它?
第二次更新:
图2a)
如果您所谓的"常规"应用程序服务与域交互,我认为将其作为域"层"的一部分是可以接受的.但是,域不应直接依赖于周围的应用程序服务,因此如果需要,可以将它们拆分为单独的模块.
我不确定你是否暗示应用层的设计(在传统的分层架构中)与使用例如Onion架构的应用层与域层合并时有什么不同?
至少就模块而言,两者应该不同,因为在这两种情况下我们都可以分离应用程序和域层模块?(虽然我必须承认我跳过模块上的章节(Evan的书),因为我认为我不需要这么早的知识来学习DDD:O)
图2b)
是的,因为它可以与分层架构形成对比.严格的分层体系结构并不适用于在域层中声明存储库接口和在基础结构层中实现的想法.这是因为一方面您在一个方向上具有依赖性,但在部署方面,依赖性在另一方面.六角形通过将域置于中心来解决这些问题.看看洋葱建筑 - 它基本上是六角形的,但可能更容易掌握.
我还不知道MVC模式或Asp.Net MVC,但无论如何,从阅读系列的前三部分(让我困惑到我停止阅读它的那一点),它出现了:
a)来自洋葱文章:
每层耦合到其下面的层,并且每层通常耦合到各种基础设施问题.
作者暗示在传统的分层架构TLA中,Domain层与Infrastructure层耦合,这当然不是真的,因为我们通常在Domain层中定义Infrastructure接口(例如Repository接口)?
b)如果在使用TLA时我们决定在Application层中定义Infrastructure接口,那么Application层也没有耦合到Infrastructure层?
c)Onion架构是否与基础架构层*耦合到Application Core(包括应用层),因为基础架构接口是在Application Core中定义的?
d)如果是c),是不是将应用层耦合到基础设施层更好(出于我在原始问题中给出的原因(这里我假设域层不会调用基础设施服务))?
4)
来自洋葱文章:
域模型周围的第一层通常是我们找到提供对象保存和检索行为的接口,称为存储库接口.
来自洋葱文章:
控制器仅依赖于应用程序核心中定义的接口.请记住,所有依赖关系都朝向中心.
似乎作者暗示,因为依赖关系只是向内,并且由于基础结构接口是围绕域模型定义的,所以域模型中的代码应该;不要参考这些接口.换句话说,我们不应该将Repository引用作为参数传递给Domain实体的方法(正如您自己所说的那样):
class Foo
{
...
public int DoSomething(IRepository repo)
{
...
var info = repo.Get...;
...
}
}
Run Code Online (Sandbox Code Playgroud)
由于上述原因,我必须承认我没有看到使用Onion架构的好处,甚至看不到它与TLA的区别(假设所有Infrastructure接口都在Domain层中定义) - >换句话说,我们不能描述TLA使用洋葱架构图?!
最终更新:
2)
b)
是.在TLA中,域层将根据基础架构层声明的类直接与基础架构通信,而不是相反.
只是为了确定 - 你说TLA的基础设施界面将在基础设施层中定义(我知道六边形/洋葱架构它们是在应用核心中定义的)?
d)
耦合是双向的.应用程序核心依赖于基础架构和基础架构中的实现,这取决于应用程
我的观点是,由于洋葱架构的InfServ接口是内声明的应用层(这里的假设是,InfServ永远不会被称为领域层,因此我们决定不定义InfServ界面内的领域层 -看到原来1A的问题),它表示应用层控制InfServ接口.但我认为,如果基础设施层控制InfServ界面会更好,原因在于原始2b问题中所述的原因?!
4)
似乎作者暗示,由于依赖关系仅是内部的,并且由于基础结构接口是围绕域模型定义的,因此域模型中的代码不应引用这些接口.
在我看来,您的代码示例是适当的代码.
所以我说Onion Architecture不允许" 域模型 " 引用基础设施接口是正确的,因为它们是在包含 DM 强文本**并从*引用它们的层InDe(InDe当然也驻留在应用程序核心内)中定义的.*DM意味着依赖关系从DM上升到InDe?
DDD通常以六边形/洋葱建筑风格呈现,这就是为什么可能存在一些混淆.我认为你可能已经做过的事情已经是六角形了,这就是为什么它看起来是同样的事情.
是的,我也有这样的印象.虽然我计划在阅读Evan的书之后,更深入地研究六角形结构(特别是因为新的DDD书将基于它的一些例子).
第四次更新:
2)
d)
如果基础设施拥有接口和实现,则域或应用层将负责为其自身实现持久性.
我假设应用程序或域层需要为自己实现它,因为引用Infrastructure层中定义的接口会使Onion的内层规则不依赖于外层(Infrastructure层是外层)?
4)
域模型可以引用基础结构接口,例如存储库,因为它们是一起声明的.如果应用层是从域中拆分的,就像在洋葱图中一样,那么域层可以避免引用接口,因为它们可以在应用层中定义.
但是根据那篇文章,Infrastructure接口是在Domain Layer周围的层中声明的,这意味着它比Domain Layer更接近应用程序核心的外边缘- 正如文章指出的那样,内层不应该依赖于外层> !
谢谢
1a) 是的,但是域层的定义可以包括充当域上的外观的应用程序服务。应用程序服务通常引用存储库和其他基础设施服务。它编排域对象和所述服务来实现用例。
2a、b)
我通常将如上所述的应用程序服务合并到域层中。我倾向于坚持六边形架构风格,也称为端口和适配器。域位于中心,由应用程序服务封装,所有外部连接(包括存储库、UI 和服务)充当端口。
2c) 在域层中声明存储库接口的部分好处是它指定了域的数据访问要求。
更新
1a) 我并不打算将我提到的应用程序服务与“常规”应用程序服务区分开来。如果它是域上的外观,则适用规则。
1b) 可能有些服务仍然可以称为应用程序服务,但与域不直接相关,我想排除这些服务。
2a)如果您所说的“常规”应用程序服务与域交互,我认为将其作为域“层”的一部分是可以接受的。但是,域不应直接依赖于周围的应用程序服务,因此如果需要,可以将它们分成单独的模块。
2b) 是的,因为它可以与分层架构进行对比。严格的分层架构并不符合在领域层声明存储库接口并在基础设施层实现的想法。这是因为,一方面,您在一个方向上有依赖性,但就部署而言,依赖性在另一个方向上。Hexagonal 通过将域置于中心来解决这些问题。看一下洋葱架构- 它本质上是六边形的,但可能更容易掌握。
2c) 是的,但通常应用程序服务会引用存储库接口。
更新2
2a) 它们的实施方式可以不同,但一般职责是相同的。主要区别在于依赖图。虽然您可以使用任一架构将应用程序服务分离到它们自己的模块中,但洋葱/六边形架构强调使用接口来声明对基础设施的依赖关系。
2ba)是的,这实际上是洋葱架构的一个特征。
b) 是的。在 TLA 中,域层将根据基础设施层声明的类直接与基础设施进行通信,而不是相反。
c) 是的,基础设施实现了领域层声明的接口。
d) 耦合是双向的。应用程序核心取决于基础设施中的实现,基础设施取决于应用程序核心中声明的接口。
4)在我看来,您的代码示例是合适的代码。在某些情况下,实体需要访问存储库才能执行某些操作。然而,为了改善此代码的耦合特性,最好定义一个特定的接口来声明实体所需的功能,或者如果 lambda 可用的话更好。然后,存储库可以实现该接口,并且应用程序服务在调用给定行为时将存储库传递给实体。这样,实体不依赖于通用存储库接口,而是依赖于非常特定的角色。
DDD 通常以六边形/洋葱架构风格呈现,这就是为什么可能会出现一些混乱的原因。我认为你可能正在做的事情已经是六角形的,这就是为什么它看起来像是同一件事。
更新3
2b) 在 TLA 中不会有接口,或者不是同一类型的接口。该域将直接与基础设施(例如持久性框架)通信,因此它将负责持久性。
2d)如果基础设施拥有接口和实现,那么域或应用程序层将负责实现其自身的持久性。在六边形/洋葱中,持久性的实现是基础设施的一部分——它将抽象域“适应”数据库。
4)领域模型可以引用基础设施接口,例如存储库,因为它们是一起声明的。如果应用层从领域中分离出来,就像洋葱图中那样,那么领域层就可以避免引用接口,因为它们可以在应用层中定义。
更新4
2d) 不,该陈述适用于具有层次结构的分层架构,例如:UI -> 业务逻辑 -> 数据访问。业务逻辑层依赖于数据访问层,并且必须基于数据访问框架的对象模型来实现其数据访问。数据访问框架本身对业务层一无所知。
4) 文章仅指定了一种可能的架构。有可接受的变化。