如何在层与层之间划分绑定到 DI 容器的依赖项?

How to divide the dependencies binding to a DI container between tiers and layers?

我正在开发一个分为 4 层的微服务应用程序(APIApplication基础设施).

我的应用程序的组合根是 API 层中的程序集。据我了解,使用 DI 框架的应用程序中依赖项的所有绑定和注册通常在组合根(也称为 API 层)中完成。

但是这种关于组合根的假设是否表明它对应用程序了解得太多了? Infrastructure 层的依赖项将在 API 层设置。那里的封装在哪里?

但是,如果每一层都可以注册自己的内部依赖项,那么它不是将特定的 DI 框架耦合到整个应用程序吗?我还需要在层之间传递容器,以便每一层都可以注册它的东西。

如何处理这种情况?

您得到的是 fundamental difference 库和框架之间的 fundamental difference 不是 规范, 规定。

使用库的初始认知负荷较小,但使用框架通常具有较高的初始认知负荷,一旦你了解了框架,它就会为你做很多事情。

好的,除此之外,我对分层架构所做的是在 IoC 方面在库中提供任何规定(因此没有注册,什么都没有)。那么,一个库应该是非常可重用的,而不用考虑太多依赖性。

对于框架,我将为注册提供 合理的默认值,并使其可覆盖。务实地说,这归结为

  • 框架使用接口的具体实现在每一层中注册它所知道的事情。这些具体实现是框架中内置的内容。
  • 框架提供“便捷方法”、抽象注册 类 或在正确的抽象级别 连接依赖关系的配置文件 以允许调用者覆盖这些默认值。

API场景

例如,一个支持 APIs 的框架需要构建相关资源的 URI,知道它需要一个上下文感知 IUriBuilder,以及一个 INavigationModel 来导航到相关资源资源;这些一起合作,允许 API 提供 link 和资源。

这是将强加于调用层的抽象级别。框架 不会 对调用者强加较低级别的抽象,因为调用者知道太多,调用者可能会破坏框架需要的东西。

假设框架提供了基本的 API-behind-a-reverse-proxy 默认值。它为您注册了所有这些。如果这还不够,您的调用者将调用一个名为 RegisterLinkBuildingService(...) 的方法。此方法通过 ConfigureServices 将任意的、单独的注册替换为强制调用者提供框架所需的特定具体服务的规范方法。

所以,你说,好吧,我需要 link 构建能够识别我的云与世界其他地方之间的所有代理、API 网关等的建筑物。您不会在 API 层注册所有这些, 因为在框架层* 需要整个表单 中的大部分内容。相反,您使用 您的 具体服务调用上面的 Register.. 方法。

这通过契约注册接口集成了两个(或更多)层,封装了需要的东西,但允许上层有一定程度的自由。

But doesn't this assumption about the composition root indicate it knows too much about the application? Dependencies to the Infrastructure layer will be set at the API layer. Where is the encapsulation there?

没有封装在你Composition Root. The Composition Root is strongly coupled to all assemblies, modules, and components in your application. But this isn't particular to the use of Loose Coupling or Dependency Injection. Due to the way how library dependencies work—they are transitive—means the startup path of your application will always (directly or indirectly) depend on all other assemblies in your application. Because of this, the start-up path should be the most volatile part of the application (and when you follow the Composition Root pattern, it will be). It should follow the Stable-Dependency Principle; one of Principles of Component Design (see chapter 28 of Agile Principles, Patterns, and Practices里面详细讨论)。

it knows too much about the application?

我会说它知道的就够了。它的工作是对象组合,这意味着它必须了解系统中所有组件的结构。它不应该知道所有的实现细节,但足以能够正确地组成系统。您可以尝试将组合移回单独的模块,但这会导致组合知识分散在整个应用程序中,而您的入口点仍然依赖于一切,因为组装依赖性的传递性质。你最终会在一个更糟糕的地方。对象组合的中心化是组合根的全部思想。

section 4.1 of DIPP&P, Mark Seemann 中,我更广泛地讨论了组合根和库依赖项的传递性质。