六边形架构教程:构建可维护的 Web 应用程序
在设计高效的 Web 应用程序时,正确的软件架构至关重要。构建可维护的 Web 应用程序的一个好方法是构建一个灵活、可扩展且适应性强的架构。六边形架构是软件开发中一种流行的架构模式。这种架构通过将逻辑划分到应用程序的不同层级来促进关注点分离。今天,我们将深入探讨六边形架构模式,并讨论其原理、优缺点、用例等。
让我们开始吧!
我们将介绍:
什么是六边形架构?
六边形架构,又称端口和适配器架构,源自 Alistair Cockburn 的研究成果。它是一种用于设计软件应用程序的架构模式。在六边形架构中,我们将输入和输出置于设计的边缘。这使得我们能够将应用程序的核心逻辑与外界隔离。由于输入和输出位于边缘,我们可以在不影响核心代码的情况下切换它们的处理程序。
六边形架构旨在提高 Web 应用程序的可维护性,从而减少代码的整体工作量。六边形架构以六边形表示。六边形的每个边代表着系统与其他系统通信的不同方式。我们可以使用 HTTP 请求、REST API、SQL 以及其他六边形架构等进行通信。六边形的每一层都独立于其他层,因此我们可以进行单独的更改而不会影响整个系统。
让我们看一下六边形架构的样子:
应用层以六边形表示。六边形内包含领域实体以及与之协同工作的用例。如我们所见,没有向外的依赖关系。所有依赖关系都指向六边形中心。六边形的内部,即领域,只依赖于自身。这确保了业务逻辑与技术层的分离,也确保了领域逻辑的复用。即使更改技术栈,也不会对领域代码产生任何影响。核心层包含主要的业务逻辑和业务规则。
在六边形的外部,我们可以看到与应用程序交互的不同适配器。不同的适配器会与应用程序的不同方面进行交互。例如,我们可以有一个与 Web 浏览器交互的 Web 适配器,一些与外部系统交互的适配器,以及一个与数据库交互的适配器。左侧的适配器驱动我们的应用程序,因为它们调用我们的应用程序核心。右侧的适配器由我们的应用程序驱动,因为它们由我们的应用程序核心调用。
适配器可以是应用程序的外部 API,也可以是其他系统的客户端。适配器使用端口发起与应用程序的交互。REST 控制器就是一个适配器的例子。应用程序核心提供端口,以便与适配器通信。端口允许我们将适配器插入核心域。我们可以将端口视为不可知的入口点。
注意:六边形架构不应依赖于任何技术框架。这包括外部注解,例如 Java 持久性 API (JPA) 和 Jackson。
优于传统分层架构
六边形架构与传统的分层架构截然不同。六边形架构的主要区别之一是用户界面可以更换。使用六边形架构替代分层架构有很多好处。让我们来看看它的一些优缺点和用例:
优点
- 可维护性:我们的应用程序具有很高的可维护性,因为应用程序一个区域的更改不会影响其他区域。
- 灵活性:我们可以轻松地在不同的应用程序之间切换,并且可以在不更改源代码的情况下添加新的适配器。
- 简单测试:由于我们的代码与外部的实现细节分离,因此我们可以进行隔离测试。
- 不可知:由于应用程序独立于外部服务,我们可以在构建外部服务之前开发内核。
缺点
- 解耦:我们的应用程序的性能可能会因为中间类而受到影响。
- 调试:有时理解和调试适配器可能很困难。
- 复杂:六边形架构有时会令人困惑,因为我们并不总是清楚应该考虑其外部的东西。
用例
六边形架构的一些示例用例包括:
- 允许我们将钱从一个账户转到另一个账户的银行应用程序
- 该系统允许我们申请贷款、进行验证,并在申请更新时收到更新
- 一个忠诚度应用程序,允许我们注册客户并升级或降级他们的会员资格
六边形架构的原理
现在,让我们来看看六边形架构背后的一些基本原理。
单一职责原则
单一职责原则的定义是“一个组件应该只有一个改变的理由”。当与架构相关时,这意味着如果一个组件只有一个改变的理由,那么如果我们因为其他任何原因更改软件,我们就不必担心这个组件。
依赖倒置
依赖倒置原则 (DIP) 允许我们在代码库中反转任何依赖项的方向。问题在于,只有当我们控制依赖项的两端时,我们才能反转依赖项。因此,如果我们依赖第三方库,我们就无法反转它,因为我们无法控制该库的代码。
让我们来实际演示一下依赖倒置原则。假设我们想要反转领域代码和持久层代码之间的依赖关系,以便持久层代码依赖于领域代码。我们将使用以下结构:
在上述结构中,我们在领域层中有一个服务,它与持久层中的存储库和实体协同工作。我们可以在领域层中为存储库创建一个接口,并让持久层中的存储库实现它。这使我们能够将领域逻辑从对持久层代码的依赖中解放出来。它看起来应该是这样的:
使用端口和适配器隔离边界
端口和适配器使我们能够以完全隔离的模式运行应用程序。六边形架构使用端口和适配器来表示内部和外部之间的通信。端口是应用程序的边界。端口有两种:主端口和辅助端口。
主端口(或入站端口)是外部世界与应用程序核心之间的初始通信点。请求通过主端口到达应用程序。次端口(或出站端口)由应用程序核心用于将数据上传到外部服务。
适配器充当端口的实现。适配器分为两种:主适配器和次适配器。主适配器是主端口的实现,它们独立于应用程序核心。次适配器是次端口的实现,它们也独立于应用程序核心。
六边形架构示例
在本文前面,我们列出了一些六边形架构的用例。现在,我们将在一个简短的教程中开始讲解其中一个用例。我们将以一个允许我们将资金从一个账户转账到另一个账户的应用程序为例。让我们先来看看创建该类所需的代码SendMoneyService
:
package buckpal.account.application.service;
@RequiredArgsConstructor
@Transactional
public class SendMoneyService implements SendMoneyUseCase {
private final LoadAccountPort loadAccountPort;
private final AccountLock accountLock;
private final UpdateAccountStatePort updateAccountStatePort;
@Override
public boolean sendMoney(SendMoneyCommand command) {
// TODO: validate business rules
// TODO: manipulate model state
// TODO: return output
}
}
下一步要学什么
恭喜您迈出了使用六边形架构的第一步!六边形架构软件设计模式创建了一个抽象层,将应用程序核心与外部工具和技术隔离开来。它是软件开发中一种流行的架构风格。接下来推荐您学习以下主题:
想要亲身体验六边形架构并完成“汇款”用例的构建,请查看 Educative 的课程《面向 Web 应用程序的六边形软件架构》。在本课程中,你将学习如何以简洁易维护的方式设计软件模块和应用程序。学习结束后,你将对如何实现软件架构有更深入的理解。
学习愉快!