【问题标题】:How loosely coupled can a Domain Model be?域模型的松耦合程度如何?
【发布时间】:2017-11-21 02:19:33
【问题描述】:

问题:我是否应该改变我的依赖关系并让DomainModel 依赖于Core(它包含系统接口和高级策略)?

我们是否考虑过用丰富的域模型来表示实现细节?

背景:所以我的DomainModel 在一个单独的项目中,没有传出依赖项。我所有的子系统服务都在单独的项目中,并且它们都不知道彼此。我还有一个Core 项目,它包含所有系统特定的东西,例如接口、枚举、验证类、扩展和其他所有服务共有的东西,我不会将它们归类为低级实现细节。 Core 除了对DomainModel 没有外向依赖,所有服务都依赖于Core 和DomainModel。

我目前的想法:所以我听说过诸如

之类的设计声明

“领域模型应该没有外向依赖”

和可能对比

“高级策略不应依赖于低级实现细节”

和

“模块应该在稳定的方向上相互依赖”

我发现当我开发系统时,领域模型的实现发生了很大变化,所有的业务规则和对象都在那里,我认为大多数领域模型根本就不是稳定的,可能其中一些是这样的作为ValueObject(s)。

优势我看到 DomainModel 依赖于 Core 的优势在于,高级策略然后对域模型的实现细节一无所知,这也与其余部分隔离系统。事实上,所有其他项目都将只依赖于Core,并且彼此之间仍然一无所知。当我对 DomainModel(我在开发时经常这样做)进行更改时,构建将只重新编译该项目,而不是像目前那样重新编译整个解决方案。

缺点我看到在实现这种额外的松散耦合时会增加复杂性?我必须为每个单独的域模型对象创建接口,这些对象将位于Core 中的一个特殊文件夹中。对于这些接口,我还想删除“I”前缀并说MoneyType : Money [接口]。我想我可以实例化域对象的唯一方法是通过抽象工厂,所以我还需要更多这些。现在我确实喜欢能够在系统中的“任何地方”实例化具体的域对象,几乎就像 BCL 的扩展或抽象(这不是域模型的一部分吗?)。

我可以想象系统可以使用任一架构,所以这只是竞争设计问题的经典案例 - 还是我完全遗漏了什么?

编辑:这个投票率很高的答案似乎表明域模型应该被隔离,每个对象只向系统的其余部分公开一个接口。

https://stackoverflow.com/a/821300/7417812

目前这对我来说感觉更好,因为系统的其余部分无法不受限制地访问域对象(因此每次发生更改时都会重新编译它们)。但是,将这种依赖关系转变为完全隔离并非易事,因此我非常愿意接受更多讨论和答案。

更多编辑:所以我尝试了重构,当我开始尝试实现我自己的简单 DI 容器时,我觉得事情开始变得很臭而且有点可笑。

完全DomainModel 隔离带来的任何好处都被复杂性和代码行数的指数级增长所抵消(至少对我而言)。所以我重构了架构,使DomainModel 只依赖于Core,它现在只通过合同类、自定义集合、注释和我使用的常见扩展来保存我的设计。我将所有特定于系统的接口和基础设施放到一个新项目Infrastructure 中,所有子系统服务都依赖它(这里没有特定技术,所以可能不是典型的基础设施层?)。

现在一切都感觉好多了,但是我又回到了依赖于 concrete 的领域对象,我可以使用这些对象。这根本不是一个浪费的练习,因为我发现了DomainModel 的一些改进和简化,并且能够澄清Core 并确定Infrastructure 的机会。

【问题讨论】:

  • 你指的是洋葱架构中的“核心”吗?
  • @ConstantinGALBENU 我实际上并不熟悉该框架,Core 只是我自己的项目名称。

标签: c# oop architecture domain-driven-design


【解决方案1】:

我还有一个核心项目,其中包含所有特定于系统的内容 诸如接口、枚举、验证类、扩展和 所有服务共有的其他东西

您正在描述不属于特定层的不可知代码结构和模式。

  • 接口和枚举,只要是关于领域概念的,都应该在领域层。其他接口和枚举...好吧,只要它们最适合。

  • “验证类”也很模糊,但域验证在域中,命令验证在应用层,用户输入验证在 UI 等。

  • 任何层都可以找到扩展方法。

我认为您应该摆脱看起来不太连贯的“核心”项目,并将其所有内容分散到适当的项目中。

IMO,所有“高级策略”都应该放在域中。由所有项目共享的实用程序类,您可以将其放在类库项目或 NuGet 包中,但这些类不应太多,我也不会称其为核心。

【讨论】:

  • Core 项目肯定缺乏连贯性这使得将所有子系统项目分开变得非常容易......我不确定将接口放入域模型项目是否是解决方案,因为那是我开始的地方......
  • I'm not sure that putting the interfaces into the domain model project is the solution - 这就是original Onion 方法的建议(我的意思是,对于域接口)。一些域接口甚至不在域的中心,而是稍微靠近边缘:域模型周围的第一层通常是我们可以找到提供对象保存和检索行为的接口。
  • I'm not sure that putting the interfaces into the domain model project is the solution - 好吧,至少它解决了你的依赖问题,对吧?
  • 我同意这不是正确的解决方案。我不会把它称为我的“问题”,更多的是寻找最佳系统质量 - 就像软件开发中的许多事情一样,它最终可能只是竞争问题之间的妥协......
【解决方案2】:

Onion architecture 是与 DDD 很好对齐的架构之一,Domain 层可以进一步拆分为两个子层:

  1. 核心:包含不特定于任何领域或技术的构建块,包含通用构建块,如列表、案例类和参与者。它永远不会包含技术概念,例如REST 或数据库。
  2. 域:所有业务逻辑所在的实际域层,其中的类和方法使用域的通用语言命名。通过 API 控制域并将所有业务逻辑放入应用程序可移植的域中,可以提取所有技术位,而不会丢失任何业务逻辑。

因此,您可以拥有一个在有界上下文之间共享的Core 组件,但我认为这必须位于单独的项目中。这取决于您希望如何将此组件的更改传播到其他项目。

关于依赖关系,Domain sub-layer 将依赖于Core sub-layer,因为它使用其低级类,但这不会破坏Dependency inversion principle,因为两者都在同一层中。但是,(整个)Domain layer 不应依赖于任何其他层,例如 Application、Presentation 或 Infrastructure,Domain layer 应该保持纯净,没有副作用和依赖关系。在任何架构中都应该遵守这条规则。

【讨论】:

  • 我喜欢它,在这个阶段这正是我正在努力的方法。 Core 和 Domain Model 感觉它们属于同一层,而不依赖于其他任何东西。由于我花时间分离出与应用程序无关的 DomainModel,因此在 Core 和 DomainModel 之间翻转依赖关系并不难,我觉得 Core 应该定义 DomainModel 插入的 API,然后所有子系统/服务都依赖于 Core。此外,通过实现 DomainModel API,它还提供了通过合约/代码合约更干净地实现设计的机会。
  • 是的,但请注意,这两个子层来自领域层,它们遵循领域层规则:纯粹、无副作用、技术无关、无 IO、无外部调用等跨度>
  • 是的。这也让我想起了我读过的关于“功能域模型”的内容。我所有的值对象都是不可变的,事实上,我努力使所有内容不可变,并且在可能的情况下只读。
  • 但是你不必让所有的东西都是不可变的。不,不。只有值对象。实体应该是可变的,但它们本身不会持久化。一个不能持久化的可变对象就像一个不可变对象:它没有副作用; immutability 不是领域层的 DDD 要求,side effect free 是
  • 没错,它就是这样工作的。不可变实体和聚合没有意义。
猜你喜欢
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-19
相关资源
最近更新 更多