洋葱与N层建筑

时间:2015-01-19 09:52:38

标签: architecture dependency-injection inversion-of-control onion-architecture n-layer

事先有一件事:我是从N层背景来的。

我现在花了很多时间来了解洋葱架构和相关的领域驱动概念,例如六角架构阅读资源,如Jeff Palermo's series of blog postsMark Seemann's contribution from a DI-perspective"Onion-izing your achitecture"和{{ 3}}

所有这些文章的共同之处在于它们声称有以下几点:

  • 专注于业务用例的域模型
  • 通过强调依赖性倒置原则来扩展层之间的耦合
  • 提高外部基础架构的独立性,例如框架,数据持久性,UI
  • 更好的可测试性/可维护性

嗯,这听起来非常好听,那些图表看起来也很甜美。但是问题出现在我身上:仅仅通过在传统的N层架构中增加外墙来实现这一切吗?

  • 每个层只知道下面层的抽象
  • 具体实现可以保持在每个层的内部,因此与抽象
  • 位于同一位置
  • 实施细节可以轻松换出,因为它们是图层内部的,不应影响应用程序的其余部分

请帮助我了解以域为中心的架构的真正优势。

提前致谢!

6 个答案:

答案 0 :(得分:6)

添加外墙实际上是从n层架构中构建洋葱架构的第一步。所以,是的,你可以立即获得许多好处。

由于您需要反转依赖关系控制,因此测试仍然存在问题。控制外观指向的内容需要转移到消费者,而不是提供者。这允许消费者将事物交换出来进行测试,或者在没有提供者必须知道的情况下更改实现。

答案 1 :(得分:3)

虽然这是一个很老的问题,但想补充一些东西。

http://php.net/language.variables.superglobals

这篇文章清楚地解释了为什么分层不是优选的,但是如果你正确地实施了IOC,它将会产生任何不同。

答案 2 :(得分:2)

我和你一样有同样的意见,我们打算开始一个新的项目,我的一个同事建议洋葱架构,但在记录了一下之后我有点困惑,因为(对于我!)每个客户层必须仅依赖于所用层的抽象这一事实是最佳实践的问题,必须始终牢记我们计划使用的任何架构,DI是“原理”而不是架构所以我无法意识到如何使用具有“良好OO原则”的N层架构使其成为一种新架构?

通过这种方式,我们将通过将所有OO Principal和Go4模式组合到Entreprise应用程序架构来结束数百个新架构。

答案 3 :(得分:1)

直接解决您的问题“仅通过在传统的N层架构中添加façades来实现所有这些目标吗?”。答案是肯定的,不,取决于您的使用案例。

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

答案 4 :(得分:1)

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

洋葱体系结构(也许这只是我自己对洋葱体系结构的解释)使我们不仅可以将类与依赖项分离,还可以将层与层分离。想想这个场景,假设您想构建Web项目的桌面版本,但使用文件存储而不是数据库存储。您将如何使用分层架构?

在两种情况下,您都必须创建DAL的新版本。但是,您将如何在业务层中解释这两个不同的层呢?在分层体系结构中,抽象位于DAL中,因此要编译业务层,它必须包含对抽象所在的层的引用。因此,您不能只换掉DAL,因为您需要带有抽象的DAL。而且,您不能只是在新的DAL中为已编译的语言复制接口,因为那样(除了代码重复),只是命名相同的内容对编译器来说就不会相同。

解决方案是将抽象转移到使用它的层。您将把抽象移到业务层。因此,在上面的示例中,您将拥有MyApp.BL/Personnel/IPersonAccessor.cs,然后您将拥有MyApp.DAL.DB/Personnel/PersonAccessor.csMyApp.DAL.FileStorage/Personnel/PersonAccessor.cs每个DAL类将实现IPersonAccessor和您的IOC容器(很可能存在于您的应用程序的演示文稿层)可以注入所需的任何PersonAccessor。这样,您的DAL将引用/依赖于业务层,以便其实现实现业务层中存在的抽象。现在,就依赖关系而言,业务层实际上可以完全隔离存在。然后,您可以简单地通过将实现注入文件存储DAL而不是数据库DAL中来替换DAL,并且由于两个DAL的类使用相同的确切接口,因此业务层可以保持不变。层。

使用这种新模式,您会得到一个奇怪的,陌生的图像。 DAL取决于业务层。这就是为什么将建筑图案视为洋葱的原因。 DAL本质上是外层的另一部分。它与表示层位于同一层。实际上,我认为它不只是DAL层和表示层...我认为它是“基础结构”层。在基础结构层内部,您拥有数据访问代码,演示代码和与外界进行通信的其他代码。然后,在这一层的下面,您的业务层将受到保护,甚至不知道外界是否存在。是洋葱一层受到多个项目/组件的另一层保护,使其免受外界影响,使它像洋葱一样。

因此,洋葱架构实际上是它自己的架构模式,因为您具有不同的层,并且界面位于不同的地方……换句话说,它们的结构也不同,按照定义,这是一种架构模式。

在仔细考虑了这种架构之后,我开始意识到这确实将您的业务层变成了一个可以扔进应用程序的库,因为它除了依赖于诸如日期时间库之类的实用程序库(例如蟒蛇)。这使您的应用程序变得非常动态,因为您可以轻松地执行诸如更改平台,更改为微服务,然后再更改为整体的操作。是的,无论如何,这都不是一个可能的场景,但是对于庞大的应用程序,这可能非常方便。您要做的就是重新编写基础架构逻辑,但您的核心业务逻辑保持不变。

答案 5 :(得分:1)

我曾经在N层架构上工作了一段时间,但是大约一年前,我们的团队决定将重点转移到Onion架构上。老实说,从一开始它就看起来非常复杂。我们有完全相同的问题,因为每个人都喜欢各层之间/内部的关系,职责等。

Internet上有大量文档,但是,如果您使用洋葱尽可能接近核心原则,那么一天结束时您会减少很多痛苦。

以下是我们决定遵循的核心原则(规则):

  • 核心(也称为域)应用于各层之间/各层之间的任何关系
  • 允许服务之间的关系(也称为用例),但应在核心中定义输入/输出契约(将其与其他接口区分开并使用特定的业务逻辑接口)
  • 基础架构没有业务逻辑,应该与所使用的应用程序无关(例如,如果您的记录器工厂在那里,则无需进行任何调整即可将其复制到其他微服务中)
  • 数据访问必须通过存储库完成,并放置在基础结构层中。尽管如此,存储库还是域和数据模型之间的一种适配器。
  • 在核心中定义数据访问接口,以便至少指定输入参数
  • 洋葱绝对独立于编程方式。它也可以很好地用于类和函数。 当您的应用程序基于域时,
  • 洋葱开始工作。每当您从逻辑领域解耦逻辑时,从功能的角度来看都不会使其逻辑化
  • 在测试中使用与在源代码中使用的目录结构相同的
  • 在整个系统中使用相同的图层名称。例如。核心层的每个地方都应该具有完全相同的名称(适用于微服务)

到目前为止,我已经注意到N层体系结构和Onion之间的区别,即您拥有一个合同的地方,这确实有助于您遵循开放/关闭原则。而且由于核心层不应该经常更改(理想情况下根本不应该更改),因此可以帮助您在代码审查期间注意它。

我创建了一个基于洋葱的简单脚手架项目。随时询问是否不清楚:)

https://github.com/YegorMedvedev/python-onion-scaffold