【问题标题】:Does Onion Architecture contradict IoC洋葱架构与 IoC 矛盾吗
【发布时间】:2013-02-28 11:35:46
【问题描述】:

Jeffrey Palermo 开创了洋葱架构,我发现这是一个很好的方法。

http://www.headspring.com/jeffrey/onion-architecture-part-4-after-four-years/

但是,如果我的理解是正确的,他的陈述“内层定义接口。外层实现接口”似乎与 IoC 相矛盾,即消费者定义接口和提供者实现它,即控制权在于消费者而不是提供者。

这个原则对我来说很有意义,因为假设您正在编写一个 UI,这个原则意味着您可以继续创建您的 UI,而无需了解您将要调用的服务,因为您负责定义接口它公开了您需要的所有功能。

因此,为此,Jeffreys 的声明似乎是矛盾的,并且让我对将合同(接口定义)放在哪里感到困惑,因为它似乎暗示: 领域层 我的实体 IMyService 服务 MyEntityService : IMyService

由于域下没有层,我将 IMyEntity 放在哪里。这也意味着在 Domain 存在并定义 IMyService 之前,我无法创建 Presentation 项目。

正如我的旁注,我应该将 IMyEntityRepository 和 MyEntityRepository 放在哪里?由于服务依赖于 IMyEntityRepository 而 MyEntityRepository 依赖于 IMyEntity

【问题讨论】:

    标签: inversion-of-control onion-architecture


    【解决方案1】:

    那么,从哪里开始呢? :-)

    让我们从国际奥委会的真正角色开始。根据Wikipedia

    控制反转是一种编程技术,其中对象 耦合在运行时由汇编程序对象绑定,通常是 在编译时未知。

    在您的情况下,您的 UI 将在不知道将在运行时绑定的服务实现的情况下操作服务接口。定义这些服务接口不是由消费者决定的;它们将在您的 Onion 架构的应用程序核心中定义,但我们稍后会看到。

    “内层定义接口,外层实现接口”,洋葱架构就是这样设计的,但别忘了最外层是IOC!由 IOC 在运行时将接口与正确的实现绑定!

    您说对了,如果没有至少一种可用于您要操作的接口的实现,您的 UI 将无法工作。但在这种情况下,如果出于任何原因需要先构建 UI,请考虑使用模拟框架!

    您的最后一个问题是您需要将 IMyEntityRepository 和 MyEntityRepository 类放在哪里。好吧,这很容易;-) IMyEntityRepository 肯定需要放在您的应用程序核心中。您的所有实体、服务接口、存储库接口以及任何需要位于同一位置的接口。 MyEntityRepository 实现应该放在你的基础设施层的某个地方,因为他的角色主要是处理从数据库中获取数据。

    希望有帮助!

    【讨论】:

    • 我认为 Wiki 的引用更多是对 IoC 后果的陈述,更适合作为 DI 的定义。据我了解,控制反转是指反转强加给服务消费者的控制,因此消费者不必适应服务,而是相反。
    • 依赖注入提供了控制反转。 DI 只是实现 IOC 的一种技术(使用工厂或服务定位器是实现 IOC 的另外两种技术)。
    【解决方案2】:

    我与 Jeffrey 合作多年,我想说 IoC 是使 Onion Architecture 成为可能的组成部分。外部依赖项的接口是在依赖项很少(如果有的话)的项目中定义的(换句话说,位于洋葱“中心”的项目)。实现那些依赖于外部依赖项的接口的类位于洋葱边缘/表面的项目中。因此,需要 IoC 容器在运行时将洋葱边缘的类实现“连接”到洋葱核心的接口。

    【讨论】:

      【解决方案3】:

      我们已经在我的项目中实现了 Onion,它在概念上非常简单。

      1. 创建一个只包含接口和 POCO 的项目,我们现在称之为 Contract
      2. 创建一个或多个项目,其中包含您的接口的实现以及您的所有第 3 方事物(例如 NHibernate 映射),我们称之为实现
      3. 从需要使用此功能的项目中添加对 Contract 的直接引用,但不要从这些项目中添加对 Implementation 的引用
      4. 在您的复合根(应用程序入口点)项目中做两件事 (1) 作为构建的一部分,将最新版本的实现复制到配置的位置(我们使用 AppSettings 进行配置,但这里有很多选项) (2) 让您的容器扫描您的实施 Dll 的配置位置

      这种方法允许您仅依赖合同,其想法是您可以切换实现,因此如果您想在未来迁移到实体框架或其他东西,您只需使用该框架重新实现实现。

      我们还将 NHibernate DLL 复制到配置的扫描位置,这使我们能够使架构具有防御性,因此很难不遵循它,因为 NHibernate 仅在应该使用它的地方可用。

      【讨论】:

        【解决方案4】:

        洋葱架构中的接口是层依赖的接口(即消耗),其实现确实由提供外层。

        更具体地说,架构本身并没有说您必须抽象接口背后的业务逻辑(对于dependency inversion principle,您可能无论如何都应该这样做,但那是另一回事了)。就是说层的依赖关系应该建模为接口,这样实现可以由外层提供。

        最好的例子是基础设施代码,尤其是数据访问。您的业​​务逻辑需要加载和存储数据,因此它定义了一个接口,它将使用。外层将提供使用 NHibernate 或 EF 或其他方式的实现。

        实际上,低层(来自 DIP;即数据访问和其他商品)位于洋葱的最外层,而 高层 em>(即业务逻辑)更接近中心。

        另请参阅http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html,它将 domainbusiness logicmore business logic 术语替换为 entities用例接口适配器,IMO 更容易推理。你离中心越远,你对用户看到和使用的东西就越具体;越靠近中心越通用;中心是那些与您的企业横向的东西,然后特定于您的应用程序的用例更多地不决定它们将如何使用或在哪个环境(哪个数据库,哪个 UI 等),然后您将在您的用例和您的技术框架(ASP.NET MVC、WCF、WPF——在非 Web 场景中——、EF、NHibernate 等)之间建立适配器

        【讨论】:

          猜你喜欢
          • 2014-06-22
          • 2015-03-17
          • 2011-10-09
          • 1970-01-01
          • 2014-10-15
          • 1970-01-01
          • 1970-01-01
          • 2012-09-01
          • 1970-01-01
          相关资源
          最近更新 更多