洋葱架构很有趣

Onion Architecture Is Interesting

在分层和六边形架构之后,是时候谈论它们的近亲了–洋葱架构最初是由杰弗里·帕勒莫(Jeffrey Palermo)在一系列帖子中介绍的。

什么是洋葱架构?

正如我们在简介中所述,洋葱架构的概念与其他两种建筑风格紧密相连:分层和六角形。 与分层方法类似,Onion Architecture使用分层的概念,但是它们有些不同:

  • 域模型层,其中我们的实体和类与它们紧密相关,例如 值对象驻留

  • 域服务层,域定义的进程驻留在其中

  • 应用程序服务层,其中包含特定于应用程序的逻辑,即我们的用例

  • 外层,用于保留外围方面的问题,例如UI,数据库或测试

  • 当这些层以圆圈表示时,每层都包裹着前一层,形成了著名的"洋葱":

    在洋葱的各层之间,有一个很强的依赖性规则:外层可以依赖于下层,但是下层中没有代码可以直接依赖于外层中的任何代码。 这本质上就是依赖反转原理,只是针对整个体系结构提出的,而不仅仅是单个类。

    正如您在图片中看到的那样,三个内部层即域模型,域服务和应用程序服务是应用程序核心的一部分。 只要在运行时提供必要的依赖关系,应用程序核心便包含运行和测试应用程序所需的所有逻辑。 这要归功于上一段中引入的依赖规则。 由于应用程序核心中没有代码依赖于外层,因此我们可以出于测试(或其他任何目的)交换UI或数据库。

    洋葱架构的本质

    对我而言,洋葱体系结构的本质是依赖反转原理的应用,层之间具有体系结构定义的优先级。 杰弗里·帕勒莫(Jeffrey Palermo)在其有关洋葱架构的原始文章中强调了很多–洋葱架构和分层架构之间的主要区别在于依赖关系的方向(以及耦合的方向)。

    正如大多数人所了解的那样,在分层体系结构中,几乎所有层都可以依赖于基础结构层,如下图所示:

    这导致一些不良的耦合。 基础架构层的更改(例如更改某些库或切换数据库提供程序)可能会在整个业务逻辑中溢出更改。 Onion Architecture旨在保护业务逻辑,从而保护依赖关系规则。 洋葱架构的扁平化图片如下所示:

    实现洋葱架构

    洋葱架构没有为我们提供有关如何实现层的直接指南,因此我们可以假设您可以自由选择所需的任何级别,即类,包,模块或其他任何级别。 但是,考虑到架构的核心是领域模型,我要说的是,您绝对应该避免将它与UI等不太重要的问题混在一起。

    关于依赖关系规则,只要内层类想与外层类进行交互,事情就归结为实现依赖关系反转原理。 为此,我们只需将外层类应在内层中实现的接口放进去:

    Jeffrey Palermo本人提供了洋葱架构的示例,因此,让我们分析他的解决方案,而不是再去玩Spring Pet Clinic。 该项目位于C#中,但是由于我们仅查看代码结构而不会阅读实现,因此您只需要知道C#名称空间大致等效于Java包即可(更多信息,请参见此处)。

    项目的整体结构如下所示(我剪切了配置文件):

    如您所见,应用程序核心与使用名称空间的基础结构,UI和测试分离。 让我们看一下Core名称空间的结构:

    核心中最突出的类是Visitor和VisitorProcessor,分别是域模型和应用程序服务层的成员。 由于核心不应该依赖于外部层,因此对VisitorBuilder和VisitorRepository的依赖关系表示为接口,这些接口在UI和基础结构层中实现。

    如果您想仔细查看该项目,可以在此处查看。

    洋葱架构的好处

  • 与DDD配合良好 –对于在域模型之上构建所有内容的体系结构,这并不让人感到惊讶

  • 定向耦合 –我们应用程序中最重要的代码无关紧要,一切都取决于它

  • 灵活性 –从内层的角度来看,您可以交换任何外层中的任何内容,并且一切正常

  • 可测试性 –由于您的应用程序核心不依赖任何其他内容,因此可以轻松快速地进行隔离测试

  • 洋葱架构的缺点

  • 学习曲线 –我不知道为什么,但是人们倾向于混淆各层之间的责任划分,尤其是损害领域模型

  • 间接 –随处可见!

  • 可能很繁重 –如果您查看Palermo的项目,则应用程序核心与框架无关,甚至不依赖Java的近亲NHibernate。 除非您想跳入XML文件(是!),否则用Java来以这种方式实现事情并非易事。

  • 什么时候应用洋葱架构?

    以洋葱建筑为例,我看到的东西与六角形非常相似。 在以请求-响应方式工作的微服务领域,您通常需要一个Repository接口和一个或两个网关,并且可以很好地保护您的业务代码免受不必要的依赖。 我不确定这是否是洋葱架构,这取决于您的解释。 另一方面,我们拥有整体,其中包含更多的依赖关系,并且服务在代码级别上相互依赖。 在这种情况下,您可能会发现更多的外部依赖关系可以替换为接口。 还有我遇到的框架难题,主要是因为Bob叔叔的著作表明Onion和Hexagonal Architectures避免了框架依赖性。 因此,我要说的是,在某种程度上,我们所有人都应该取材于Onion Architecture,但该程度应视具体情况而定。

    我发现有关Onion Architecture的一件有趣的事情是,至少从我发现的博客文章来看,它对C#程序员比Java程序员更具吸引力。 当然,这只是出于好奇,在讨论是否应用该体系结构时,我不会考虑这一论点。

    摘要

    洋葱架构在各层之间设置了明确的依赖性规则,使其成为分层架构和六角架构的限制性更强的变体。 域应用程序位于我们应用程序的最中心,周围是域服务和应用程序服务。 这三层构成了应用程序核心–任何相关的应用程序逻辑都不应驻留在其外部,并且应独立于所有外围问题,例如UI或数据库。 作为Hexagonal的近亲,Onion Architecture当然是非常强大的,但是我们应该在多大程度上应用其原理。