在设计一个有效的网络应用程序时,重要的是要把你的软件架构做好。构建一个可维护的网络应用程序的好方法是建立一个灵活、可扩展和可适应的架构。六边形架构是软件开发中一种流行的架构模式。这种架构风格通过将逻辑放到应用程序的不同层来促进关注点的分离。今天,我们将深入研究六边形架构模式,讨论原则、利弊、用例等。
让我们开始吧!
什么是六边形架构?
六边形架构,或称端口和适配器架构,源于Alistair Cockburn的工作。它是一种用于设计软件应用的架构模式。通过六边形架构,我们把我们的输入和输出放在设计的边缘。这使我们能够将应用程序的中心逻辑与外部世界隔离。由于我们的输入和输出都在边缘,我们可以在不影响核心代码的情况下切换其处理程序。
六角架构的目的是提高我们的网络应用程序的可维护性,这样我们的代码在整体上需要更少的工作。六边形架构用一个六边形来表示。六边形的每个不同侧面都代表了我们的系统与其他系统进行通信的不同方式。我们可以使用HTTP请求、REST API、SQL、其他六边形架构等进行通信。六边形的每一层都是独立于其他层的,所以我们可以在不影响整个系统的情况下进行个别改变。
让我们来看看六边形架构可能是什么样子的。
应用层被表示为一个六边形。在这个六边形中,我们有我们的领域实体和与它们一起工作的用例。正如我们所看到的,没有传出的依赖关系。我们所有的依赖关系都指向中心。六边形的内部,或者说领域,除了它自己,什么都不依赖。这确保了业务逻辑与技术层的分离。这也确保了我们可以重复使用领域逻辑。如果我们改变了我们的堆栈,将不会对域的代码产生影响。核心部分持有主要的业务逻辑和业务规则。
在六边形的外面,我们看到不同的适配器与我们的应用程序进行交互。不同的适配器将与应用程序的不同方面进行交互。例如,我们可以有一个与网络浏览器交互的网络适配器,一些与外部系统交互的适配器,以及一个与数据库交互的适配器。左边的适配器驱动我们的应用,因为它们调用我们的应用核心。右边的适配器是由我们的应用程序驱动的,因为它们被我们的应用程序核心所调用。
适配器是你的应用程序的外部API或其他系统的客户端。适配器使用端口来启动与应用程序的交互。一个REST控制器就是一个适配器的例子。应用核心提供端口,以便它能与适配器通信。端口允许我们将适配器插入到核心域中。我们可以把端口看作是不可知的入口点。
注意:六边形架构不应该依赖于任何技术框架。这包括外部注释,如Java Persistence API(JPA)和Jackson。
相对于传统分层架构的好处
六角架构是对传统分层架构的一种背离。六边形架构的一个主要区别是,用户界面可以被换掉。使用六边形架构而不是分层架构有很多好处。让我们来看看其中的一些优点、缺点和用例。
优点
- 可维护性:我们的应用程序有很高的可维护性,因为我们应用程序的一个区域的变化不会影响其他区域。
- 灵活性:我们可以在不同的应用程序之间轻松切换,我们可以在不改变源代码的情况下添加新的适配器。
- 简单的测试:由于我们的代码与外部的实现细节分离,我们可以隔离测试。
- 不可知性:由于应用程序独立于外部服务,我们可以在构建外部服务之前开发内部核心。
缺点
- 解耦:由于中间类的存在,我们的应用程序的性能可能会受到影响。
- 调试:有时很难理解和调试适配器。
- 复杂:六边形架构有时会让人感到困惑,因为我们应该考虑的外面的东西并不总是那么明显。
使用案例
六边形架构的一些用例包括:
-
允许我们将钱从一个账户汇到另一个账户的银行应用程序
-
一个允许我们申请贷款的系统,通过验证,并在我们的申请更新时收到更新信息
-
一个忠诚度应用程序,允许我们注册客户并升级或降级他们的会员资格。
六边形架构的原则
现在,让我们看一下六边形架构背后的一些基本原则。
单一责任原则
单一责任原则的定义是 "一个组件应该只有一个变化的理由"。当与架构相关时,这意味着如果一个组件只有一个改变的理由,那么如果我们因为其他原因而改变软件,我们就不必担心这个组件。
继续学习
学习六边形架构,不用刷视频或文档。Educative的基于文本的课程很容易浏览,并具有实时编码环境,使学习快速而高效。
依赖性反转
依赖性反转原则(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
}
}