31

事先做一件事:我来自 N 层背景。

我现在花了很多时间来了解洋葱架构和相关的领域驱动概念,例如六边形架构阅读资源,如Jeff Palermo 的系列博客文章Mark Seemann 从 DI 角度的贡献“洋葱化你的架构”,和“干净的建筑”

所有这些文章的共同点是它们声称以下几点:

  • 重点围绕业务用例的域模型
  • 通过强调依赖倒置原则使层之间的耦合更松散
  • 增加外部基础设施的独立性,例如框架、数据持久性、UI
  • 更好的可测试性/可维护性

好吧,这一切听起来都非常好,而且这些图表看起来也很可爱。但是出现在我面前的问题是:所有这些不都是通过在我的传统 N 层架构中添加立面来实现的吗?

  • 每一层只知道下一层的抽象
  • 具体实现可以保留在每一层的内部,因此与抽象位于同一位置
  • 实现细节可以很容易地换出,因为它们在层内部并且不应该影响应用程序的其余部分

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

提前致谢!

4

6 回答 6

8

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

测试仍然存在问题,因为您需要反转依赖控制。控制外观所指向的内容需要转移给消费者,而不是提供者。这允许消费者交换东西以进行测试,或者在提供者不必知道的情况下更改实现。

于 2015-01-19T14:28:28.997 回答
3

我和你的观点相同,我们计划启动一个新项目,我的一位同事建议使用洋葱架构,但在记录了一些关于它的内容后,我有点困惑,因为(对我来说!)每个客户端层必须仅依赖于所用层的抽象这一事实是最佳实践问题,必须始终牢记我们计划使用的任何架构,DI 是“原则”而​​不是架构,所以我不能不知道如何将 N 层架构与“良好的 OO 原则”一起使用使其成为新架构?

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

于 2016-03-24T10:35:28.910 回答
3

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

洋葱与分层

这篇文章清楚地解释了为什么分层不是可取的,但如果你正确地实现了 IOC,它不会有任何区别。

于 2017-04-06T12:04:05.130 回答
2

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

互联网上有大量的文档,但是,如果你尽可能多地使用接近核心原则的洋葱,那么在一天结束时你会感觉不那么痛苦。

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

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

到目前为止,我已经注意到 N 层架构和 Onion 之间的区别,你有一个单一的地方来存放合约,它确实可以帮助你遵循 Open/Close 原则。而且由于核心层不应该经常更改(理想情况下根本不应该更改),它可以帮助您在代码审查期间注意它。

我创建了一个基于 Onion 的简单脚手架项目。看看并随时询问是否有不清楚的地方:)

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

于 2020-05-06T15:30:33.723 回答
1

直接解决您的问题“仅通过在我的传统 N 层架构中添加外墙就可以实现所有这些吗?”。答案是肯定的,也不是,具体取决于您的用例。

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

于 2017-03-14T00:01:42.913 回答
1

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

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

在这两种情况下,您都必须创建 DAL 的新版本。但是,您将如何在业务层中解释这两个不同的层?在分层架构中,抽象位于 DAL 中,因此为了编译业务层,它必须包含对存在抽象层的引用。因此,您不能只换掉 DAL,因为您需要具有抽象的 DAL。而且你不能只是在新的 DAL 中为编译语言复制接口,因为(除了代码的重复)只是命名相同的东西并不能使它与编译器相同。

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

使用这种新模式,您会得到一个奇怪的、不熟悉的图像。DAL 依赖于业务层。这就是为什么架构模式被认为是洋葱。DAL 本质上是外层的另一部分。它与表示层存在于同一层。事实上,我认为这与其说是 DAL 层和表示层......我认为它是“基础设施”层。在基础设施层内,您拥有数据访问代码、表示代码和其他与外部世界通信的代码。然后,在这一层之下,甚至不知道外部世界的存在,是您的业务层。这是一个洋葱。一层由多个项目/组件的另一层保护免受外界影响,使其像洋葱一样。

所以,洋葱架构实际上是它自己的架构模式,因为你有不同的层,你的接口在不同的地方......或者换句话说,它们的结构不同,根据定义,这是一种架构模式。

在思考了这个架构之后,我开始意识到这真的把你的业务层变成了一个更多的库,它可以被扔进和扔出应用程序,因为它除了像 datetime 库这样的实用程序库(比如在 python 中)之外没有其他依赖项。这使您的应用程序极具动态性,因为您可以轻松地执行诸如更改平台、更改为微服务,然后可能更改回单体应用程序之类的事情。是的,无论如何这都不是一个可能的场景,但对于大型应用程序来说,这可能非常方便。您所要做的就是重写您的基础架构逻辑,但您的核心业务逻辑保持不变。

于 2020-04-21T12:46:10.923 回答