Architecture 洋葱vs.N层体系结构

Architecture 洋葱vs.N层体系结构,architecture,dependency-injection,inversion-of-control,onion-architecture,n-layer,Architecture,Dependency Injection,Inversion Of Control,Onion Architecture,N Layer,事先有一件事:我来自一个N层的背景 我现在已经花了相当多的时间来了解洋葱架构和相关的领域驱动概念,如六边形架构,阅读资源,如、和 所有这些条款的共同点在于,它们主张以下几点: 重点放在业务用例的领域模型上 通过强调依赖倒置原则,层之间的耦合更加松散 增强了外部基础架构(如框架、数据持久性和UI)的独立性 更好的可测试性/可维护性 嗯,这一切听起来非常好,这些图表看起来也很可爱。但我面临的问题是:这一切不是仅仅通过在我的传统N层架构中添加立面来实现的吗 每个层只知道下面层的抽象 具体实现可以

事先有一件事:我来自一个N层的背景

我现在已经花了相当多的时间来了解洋葱架构和相关的领域驱动概念,如六边形架构,阅读资源,如、和

所有这些条款的共同点在于,它们主张以下几点:

  • 重点放在业务用例的领域模型上
  • 通过强调依赖倒置原则,层之间的耦合更加松散
  • 增强了外部基础架构(如框架、数据持久性和UI)的独立性
  • 更好的可测试性/可维护性
嗯,这一切听起来非常好,这些图表看起来也很可爱。但我面临的问题是:这一切不是仅仅通过在我的传统N层架构中添加立面来实现的吗

  • 每个层只知道下面层的抽象
  • 具体实现可以保持在每个层的内部,因此与抽象处于同一位置
  • 实现细节可以很容易地调出,因为它们是层内部的,不应该影响应用程序的其余部分
请帮助我了解以域为中心的体系结构的真正优势


提前谢谢

添加正面实际上是从n层架构构建洋葱架构的第一步。所以,是的,你可以马上得到很多好处


测试仍然存在问题,因为您需要反转依赖项控件。控制门面所指向的内容需要转移到消费者而不是提供者。这使得消费者可以交换东西进行测试,或者在提供商不必知道的情况下更改实现。

我和你的观点相同,我们计划启动一个新项目,我的一位同事建议使用洋葱架构,但在记录了一点关于它的信息后,我有点困惑,因为(对我来说!)每个客户端层必须只依赖于所用层的抽象这一事实是一个最佳实践问题,无论我们计划使用什么体系结构,都必须始终牢记这一点,DI是一个“原则”而不是一个体系结构,所以我无法意识到如何将N层体系结构与“良好的OO原则”结合使用让它成为一个新的架构


通过这种方式,我们只需将所有OO原则和Go4模式结合到企业应用程序架构中,就可以得到数百种新的架构。

为了直接回答您的问题,“这一切不是仅仅通过在我的传统N层架构中添加外观来实现的吗?”。答案是肯定和否定,这取决于您的用例


洋葱架构的重点是使用依赖项反转,正如您所说的。。。“创建更松散的耦合”。然而,这不仅仅是为了失败者的结合。将其视为“保护代码中最不可能更改的部分,而不是更可能更改的部分”,可能会有所帮助。那么,对于您的情况,外观下面的更改是否需要更改您的“域”代码?比如说,对数据库对象的更改是否会影响到外观中使用的对象的更改,然后影响到您的“域”代码?如果这些类型的问题的答案是“否”,那么您的假设是正确的,该代码没有任何有意义的功能差异。如果有人回答“可能”,那么他们可能会受益于从外观到IOC的重构

虽然这是一个很老的问题,但我想补充一点


本文清楚地解释了分层架构不可取的原因,但如果您正确地实现了IOC,它不会有任何区别。

我在洋葱架构和分层架构中发现的主要区别是抽象的位置。当我想到分层体系结构时,我经常看到的模式是界面和实现彼此相邻。假设您在MyApp.DAL/personal中有一个IPersonAccessor接口(我来自C#),那么您将有一个实现IPersonAccessor的相应PersonAccessor类。这很好,因为它允许您在测试中切换实现,但它并没有真正地将其进一步解耦

洋葱架构(可能这只是我自己对洋葱架构的解释)允许我们不仅将类与依赖项分离,而且将层与层分离。考虑一下这个场景,假设您希望构建web项目的桌面版本,但使用文件存储而不是数据库存储。使用分层体系结构,您将如何做到这一点

在这两种情况下,您都必须创建DAL的新版本。但是,您如何解释业务层中这两个不同的层呢?在分层体系结构中,抽象在DAL中,因此为了使业务层能够编译,它必须包含对抽象所在层的引用。所以你不能仅仅交换DAL,因为你需要有抽象的DAL。你不能在新的DAL中复制编译语言的接口,因为(除了代码的复制之外)命名相同的东西并不能使编译器使用相同的东西

解决方案是将抽象移到使用它的层。您可以将抽象移到业务层。因此,在上面的示例中,您将拥有
MyApp.BL/personal/IPersonAccessor.cs
,然后您将拥有
MyApp.DAL.DB/personal/PersonAccessor.cs
以及
MyApp.DAL.FileStorage/personal/PersonAccessor.cs
,每个DAL类都将实现IPersonAccessor和IOC容器(最有可能存在于您的表示层中)因为您的应用程序可以插入它想要的任何PersonalAccessor