六边形架构教程:构建可维护的Web应用程序 - DEV


在设计有效的 Web 应用程序时,让您的软件架构正确很重要。构建可维护的 Web 应用程序的一个好方法是构建灵活、可扩展和适应性强的架构。六边形架构是软件开发中流行的架构模式。这种架构风格通过将逻辑放入应用程序的不同层来促进关注点分离。今天,我们将深入研究六边形架构模式,并讨论原理、优缺点、用例等。
六边形架构或端口和适配器架构源于 Alistair Cockburn 的工作。它是一种用于设计软件应用程序的架构模式。使用六边形架构,我们将输入和输出置于设计的边缘。这使我们能够将应用程序的中心逻辑与外部世界隔离。由于我们的输入和输出位于边缘,我们可以在不影响核心代码的情况下切换它们的处理程序。
六边形架构旨在提高我们 Web 应用程序的可维护性,以便我们的代码总体上需要更少的工作。六边形架构由六边形表示。六边形的每一面都代表我们的系统与其他系统通信的不同方式。我们可以使用 HTTP 请求、REST API、SQL、其他六边形架构等进行通信。六边形的每一层都独立于其他层,因此我们可以在不影响整个系统的情况下进行单独的更改。
让我们来看看六边形架构可能是什么样子:

应用层表示为六边形。在六边形中,我们有我们的领域实体和使用它们的用例。如我们所见,没有传出依赖项。我们所有的依赖都指向中心。六边形的内部,或域,只取决于它自己。这确保了业务逻辑与技术层分离。它还确保我们可以重用域逻辑。如果我们改变我们的堆栈,它不会对域代码产生影响。核心拥有主要的业务逻辑和业务规则。
在六边形之外,我们看到与我们的应用程序交互的不同适配器。不同的适配器将与应用程序的不同方面进行交互。例如,我们可以有一个与 Web 浏览器交互的网络适配器,一些与外部系统交互的适配器,以及一个与数据库交互的适配器。左侧的适配器驱动我们的应用程序,因为它们调用我们的应用程序核心。右侧的适配器由我们的应用程序驱动,因为它们由我们的应用程序核心调用。
适配器可以是应用程序的外部 API,也可以是其他系统的客户端。适配器使用端口来启动与应用程序的交互。REST 控制器将是适配器的一个示例。应用程序核心提供端口,以便它可以与适配器进行通信。端口允许我们将适配器插入核心域。我们可以将端口视为不可知的入口点。
六边形架构不应该依赖于任何技术框架。这包括外部注释,例如 Java Persistence 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
 }
}

恭喜您迈出了使用六边形架构的第一步!六边形架构软件设计模式创建了一个抽象层,将应用程序的核心与外部工具和技术隔离开来。它是软件开发中流行的架构风格。