繁体   English   中英

.NET Core 洋葱架构中的依赖注入

[英]Dependency Injection in onion architecture for .NET Core

我正在尝试在 .NET Core 3.1 Web API 项目中实现洋葱架构,并将 EntityFramework 作为 ORM。

由于我只是在学习洋葱架构,因此我在如何遵循某些领域的规则方面遇到了一些问题。 其中之一是依赖注入。

假设我们有以下环(从外到内):

  • 基础设施(API、持久性)
  • 申请(服务)
  • 领域(模型 + 领域服务)

根据我在洋葱架构中的理解,同一层中的不同关注点不应相互依赖。 因此,在基础设施环中,更具体地说,在 API 项目中,我了解如何为我的服务连接 DI,因为这些服务(接口和实现)位于较低的环中。 但是,我还需要为同一个环的 Persistence 项目中定义的 DbContext 设置 DI。 此外,也将位于同一个环中的其他第三方工具也需要通过 DI 连接。

我看到有两种解决方案:

  1. 使 API 项目依赖于 Persistence 项目(以及其他第三方项目)。 据我所知,这打破了洋葱架构规则。
  2. 将基础设施环分成两个环,其中下半部分将包含 Web API、Persistence 和任何其他项目,但上半部分将是专用的 DI 层,它将设置所有 DI。 我相信这个外层被称为组合根。

以不同的方式总结我的问题:在洋葱架构中,如果您使用 DI,DI 看起来应该在基础设施环中,并且看起来 DI 需要引用基础设施环中的所有内容。 DI 是否必须作为洋葱架构中的异常处理? 如何处理?

这是我的理解:

编译时,域不依赖于任何东西。 因此,例如,域将不依赖于持久性项目。 这将取决于域本身内定义的抽象。

您的持久性(例如,具体的存储库)将满足域中定义的依赖项。 在现实世界中,这通常意味着他们将实现域中定义的接口。

组合根然后配置应用程序,以便具体实现与域中定义的抽象相匹配。 这就是DI配置。

这是最简单的版本。 如果需要,您可以增加更多的复杂性。 唯一不会改变的部分是域是自包含的。 如果需要,这里有一些其他方式可以增加复杂性:

  • 依赖项的实现也可以是自包含的,就像域一样。 另一个项目使它们适应域接口。 因此您的存储库不会直接实现域接口。
  • 域抽象的配置——将它们映射到它们的具体依赖项——可以分开。 换句话说,您的容器设置可以与应用程序主机分开。 理想情况下,应用程序主机将继续负责读取其配置或环境值并将它们传递给该容器配置。 这可以防止容器配置与 JSON 或 .config 文件等细节耦合。 测试更容易。

但关键的细节是,在任何情况下,域本身都是自包含的。 我们不会将域外定义的接口注入域类。 域定义了它自己的抽象,然后在域外定义的类实现了这些抽象。


您的 API 可以依赖于持久性项目。 您的 API 不是域。 您的 API 启动将依赖于域和持久性,并配置 DI 容器以提供来自持久性项目的类来实现域中定义的依赖项。

API 就像域的对立面。 域不依赖于它本身之外的任何东西。 其他依赖项指向内部。 例如,存储库实现域定义的抽象。

另一方面,API 最终取决于一切。 它使用 DI 配置向域提供依赖项(如持久性),这意味着它将依赖所有这些。


这是(在我看来)思想上最大的变化:

我们倾向于编写用于数据访问的具体类、调用外部 API 以及执行域外围的其他事情。 然后我们把接口放在它们上面。 这些接口通常看起来像是事后的想法。 这就像我们只是在创建反映这些类的接口。 如果我们在类中添加一些东西,我们就会将它添加到接口中。

然后我们获取这些接口并开始将它们注入到我们的域中。 这就是事情变得混乱的地方。 这些接口与我们的领域无关。 它们只是域外存在的类的镜像。 它使域更难测试。 违反了接口隔离。 我们发现自己在嘲笑与我们的领域无关的接口部分。

如果我们从依赖于它们的领域类的角度来设计我们的抽象,所有这些都会消失。 例如,如果我们的域类需要从存储库中检索数据,我们定义一个存储库,该存储库准确地对域类需要的内容进行建模——仅此而已。 我们不会将一些通用的通用存储库接口塞进我们的域中。

我们可能还有一些通用的通用存储库。 没关系。 但是我们将其调整为该域存储库接口。 域不知道它。 它只知道域中定义的抽象。 域类依赖于它为自己的目的定义的小的、隔离的抽象。 它拥有它们。 这使得它保持简单,并使其非常容易测试。

这是我如何解决我的情况。 我认为它非常干净,尽管在我可以安全地采用此解决方案之前,我必须测试更多场景/用例。

除了我的 API 项目,我还创建了一个 DI 项目。 这个 DI 项目是组合根项目。 它依赖于一切(应用程序、域等)。它是基础设施环的上部(外子环)。 然后,在我的 API 项目中,我引用 DI 项目并从那里调用扩展方法以将我的所有合同与实现联系起来。 此外,我不得不为其他东西添加第二个 Web 应用程序作为同一解决方案的一部分,并且我能够使用相同的方法,这很好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM