C# .NET内核洋葱架构中的依赖项注入

C# .NET内核洋葱架构中的依赖项注入,c#,.net-core,dependency-injection,architecture,onion-architecture,C#,.net Core,Dependency Injection,Architecture,Onion Architecture,我试图在一个.NETCore3.1WebAPI项目中实现onion体系结构,该项目使用EntityFramework作为ORM 因为我只是在学习洋葱架构,所以对于如何在某些领域遵循它的规则,我有一个小问题。其中之一是依赖注入 假设我们有以下环(从外到内): 基础架构(API、持久性) 应用程序(服务) 域(模型+域服务) 根据我在洋葱架构中的理解,同一层中的独立关注点不应该相互依赖。因此,在基础设施环中,更具体地说,在API项目中,我了解如何为我的服务连接DI,因为这些服务(接口和实现)位于

我试图在一个.NETCore3.1WebAPI项目中实现onion体系结构,该项目使用EntityFramework作为ORM

因为我只是在学习洋葱架构,所以对于如何在某些领域遵循它的规则,我有一个小问题。其中之一是依赖注入

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

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

对此,我有两种解决方案:

  • 使API项目依赖于持久性项目(以及其他第三方项目)。这打破了我所理解的洋葱架构规则
  • 将基础设施环拆分为两个环,其中下半部分将包含Web API、持久性和任何其他项目,但上半部分将是专用的DI层,用于设置所有DI。我相信这个外层叫做合成根

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

    以下是我的理解:

    编译时,域不依赖于自身以外的任何东西。因此,例如,域将不依赖于持久性项目。它将依赖于域本身中定义的抽象

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

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

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

    • 依赖项的实现也可以是自包含的,就像域一样。还有一个项目使它们适应域接口。因此,您的存储库不会直接实现域接口
    • 域抽象的配置——将它们映射到它们的具体依赖项——可以分开。换句话说,容器设置可以与应用程序主机分离。理想情况下,应用程序主机仍将负责读取其配置或环境值,并将它们传递给该容器配置。这样可以防止容器配置与JSON或.config文件等细节耦合。它更容易测试
    但关键的细节是,在任何情况下,域本身都是自包含的。我们不会将域外定义的接口注入域类。域定义自己的抽象,然后在域外定义的类实现这些抽象


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

    API类似于域的反面。域不依赖于自身之外的任何东西。其他依赖项指向内部。例如,存储库实现由域定义的抽象

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


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

    我们倾向于为数据访问编写具体的类,调用外部API,以及做其他与域无关的事情。然后我们把接口放在它们上面。这些接口通常看起来像是事后诸葛亮。就像我们只是在创建镜像这些类的接口。如果我们向类中添加了一些内容,我们就会将其添加到接口中

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

    如果我们从依赖于抽象的域类的角度来设计抽象,所有这些都会消失。例如,如果我们的域类需要从存储库中检索数据,那么我们将定义一个存储库,该存储库将精确地建模该域类所需的内容—不多也不少。我们不会采用一些通用的多用途存储库接口并将其塞进我们的域中

    我们可能仍然有一些通用的通用存储库。没关系。但我们将其调整为该域存储库接口。域名不知道这件事。它只知道域中定义的抽象。域类依赖于它为自己的目的定义的小型、分离的抽象。