六边形建筑:简洁建筑的实用指南
您是否曾在软件升级时遇到过难题? 您是否能够区分功能测试和集成测试?迁移遗留系统是否意味着要从头开始重写所有内容?
了解一下六边形架构(也称为端口和适配器架构)如何帮助您!
业务逻辑过头了!
在之前的一次经历中,我的团队需要将一个旧应用程序移植到全新的技术栈上。该软件从 EAR/SQL 应用迁移到使用 NoSQL 的独立 JAR 包。通过研究,我们很快意识到必须重写整个基础架构。事实上,唯一不需要改变的只有业务逻辑。所以,重用业务逻辑是合理的,对吧?
深入分析后发现,名为 model 的 Maven 模块完全由只有 getter 和 setter 的 POJO 组成,功能极其匮乏……虽然也存在一个服务模块,但业务逻辑却在所有层级间共享。它被大量技术代码淹没,例如 DAO 创建、序列化等等。根本无法提取出业务逻辑!其中一部分甚至依赖于我们试图移除的旧框架的技术行为。而这一切的根源在于:业务逻辑和技术代码之间没有清晰的分离。
关注点分离:隔离业务逻辑
由Alistair Cockburn创建的六边形架构通过使其与技术无关,确保了业务逻辑的可重用性。因此,更改技术栈不会对领域代码产生任何影响。
这种架构的一个关键概念是将所有业务 逻辑放在一个名为“领域”的单一位置。让我们更新一下之前的架构图:
重要约束:领域本身不依赖于任何其他层;这是确保业务逻辑与技术层解耦的唯一方法。我们如何在之前的模式中实现这一点呢?领域显然依赖于持久层!其实,我们可以利用一种你可能熟悉的模式:控制反转。如果将领域命名为“六边形”,它看起来会是这样:
控制权的反转非常神奇,我们稍后再来看看它是如何运作的。现在你已经对六边形架构有了大致的了解:
- 只有两个世界,六边形内部和外部。内部:所有 业务逻辑;外部:基础设施——也就是所有技术代码。
- 依赖关系总是从六边形外部指向内部。这确保了业务领域与技术部分的隔离。
- 由此可得出一个推论:Hexagon 只依赖于自身。这不仅指它自身的层,更在于它不能依赖于任何技术框架,包括 Jackson 或 JPA 等外部注解。
该领域(几乎)不允许使用任何框架。
让我再详细解释一下最后一点,这一点非常重要。在另一个案例中,我的团队需要将一个应用程序从“经典”的 Spring 框架移植到 Spring Boot。我们遇到的主要(也是最棘手的)问题是:过度依赖 Spring Integration Test 来验证我们的功能。此外,这些功能与 Spring 的耦合度也过高。
第一次尝试 Spring Boot 迁移时,所有功能测试都失败了。我们无法确定是业务逻辑出了问题,还是纯粹的技术原因。最终我们发现是测试层面的集成问题。于是,我们只能祈祷一切顺利,然后逐个修复所有测试……希望领域定义仍然正确。
Hexagon架构不使用任何框架,这意味着无论技术栈如何变化,业务领域都可以复用 。由于不再涉及集成问题,这将提高领域的可测试性。最终,得益于Hexagon架构的这一约束,您可以进行真正的功能测试。这样,功能测试将直接且仅与Hexagon交互。
注意:在 Maven 中,您可以使用enforcer 插件来确保此约束。
六边形架构的控制反转
还记得控制反转吗?为了确保六边形的隔离性,它对下游层的依赖关系已经反转。其实诀窍很简单,正如你所看到的:
六边形(基础设施)的外部被分为左右两个虚拟部分。左侧包含所有查询域的组件(控制器、REST 层等)。右侧包含所有向域提供信息/服务的组件(持久层、第三方服务等)。
使用反腐败层保护域名
为了允许外部与领域进行交互,Hexagon 提供了分为两类的业务接口:
- API收集了所有需要查询该领域的接口。这些接口由 Hexagon 实现。
- 服务提供商接口 (SPI )汇集了域从第三方检索信息所需的所有接口。这些接口在 Hexagon 中定义,并由基础设施的右侧实现。我们将看到,在某些情况下,Hexagon 本身也可以实现 SPI。
这里有两点重要的事实:
- API 和 SPI 是 Hexagon 的组成部分。
- API 和 SPI 仅操作 Hexagon 的域对象。这确实确保了隔离性。
在分层架构中,业务对象或服务通常会创建 DAO。而在六边形架构中,领域层只处理领域对象。因此,持久层负责将领域对象转换为需要持久化的 DAO。这被称为适配。
六边形建筑的模块化优势
如上所述,端口和适配器架构是六边形架构的另一种名称。它的优势在于其模块化特性。由于所有组件都相互解耦,因此您可以同时在域前端部署 REST 层和 JMS 层,而不会对其造成任何影响。
在 SPI 端,您可以根据需要将 MongoDB 驱动程序实现更改为 Cassandra。由于更改持久化模块不会导致 SPI 本身发生变化,因此软件的其余部分不会受到影响。API 和 SPI 是端口,而使用或实现这些端口的基础架构模块则是适配器。
如何实现六边形架构?
还有一条规则:永远从六边形的内部开始。这样做会给你带来很多好处:
- 专注于功能而非技术细节,因为只有功能才能为公司带来价值。一位从事其他业务领域的开发人员或许能够部署 Spring Controller,但除非他曾在会计公司工作过,否则“双倍余额递减法”对他来说就像天书一样晦涩难懂。
- 推迟技术实现方案的选择。有时,一开始很难确定真正需要哪种技术实现方案。因此,推迟选择方案有助于您专注于为公司带来核心价值的功能。此外,在业务逻辑实现之后,一些新的因素可以帮助您做出最佳的基础设施选择。例如,您可能会发现业务领域比预期更具关系性,因此 SQL 数据库是一个不错的选择。
- 由此可得出一个推论:它确保了Hexagon 是一个独立运行的程序。由于代码必须经过测试才能运行,这意味着Hexagon 本身就具备自测试能力。此外,我们还获得了真正专注于业务的功能测试。
测试驱动实现
借助六边形架构,您可以将功能测试放在领域代码中。这些测试将直接调用领域 API,从而避免任何技术层面的干扰。从某种意义上说,您创建了一个适配器来模拟控制器,从而测试领域代码的功能。
首先从领域功能测试开始。
我的建议是先使用行为驱动开发方法编写功能场景来描述你的功能。
“双循环”的第一步是使用 ATDD 生成功能测试,并编写 API 接口作为该功能的入口点。然后使用 TDD 实现测试,最后实现业务逻辑。编写过程中,您可能需要从数据库中检索一些数据,因此需要创建一个 SPI。由于右侧部分尚未实现,请在 Hexagon 中创建一个 SPI 的桩实现。这可以通过使用 Map 实现的内存数据库来实现。
您可以选择将桩对象保留在应用程序的测试范围内。当然,您也可以根据需要临时发布它们。例如,我们在 Hexagon 上开发第一个功能时,就对外部服务和数据库进行了桩化。由于客户需要我们提供接口契约,我们随后通过 REST 控制器导出了域。因此,我们发布了一个包含桩化数据的初始版本,该数据位于基础设施的右侧。这样,客户就能看到我们的数据结构和功能的预期行为。这比手动创建请求和响应的 JSON 示例要可靠得多,因为它能够真正处理实际的业务约束。
最后是适配器部分
下一步通常是先打开左侧面板。这样您就可以对该功能进行一些集成测试。此时,您可以提供一些实时文档,并确保与客户达成接口协议。
最后,通过利用集成测试来实现您功能的 SPI,即可在右侧打开。我强烈建议您的测试是独立的,以避免构建时出现任何不稳定性。您应该始终使用类似Wiremock 的工具来模拟第三方服务,或者使用Fongo来模拟 MongoDB。
对其他功能也采用相同的循环方式。
有关更多信息,请参阅 GitLab 上提供的六边形架构测试策略,该策略也在上面的演讲中进行了描述🇫🇷!
六边形建筑简述
现在我们已经了解了六边形架构!将业务逻辑与技术代码解耦确实有很多好处。它能确保您的业务领域在技术不断演进的过程中保持持久性和稳健性。
六边形建筑为您提供了一种实现这一目标的切实途径:
- 将所有业务逻辑放在一个地方。
- 该领域在技术层面上是隔离且无关的,因为它只依赖于自身。这就是为什么依赖关系总是从六边形外部指向内部的原因。
- Hexagon是一个独立模块。因此,它可以通过编写无需处理技术问题的真正功能性测试来提高领域代码的可测试性。
- 这种架构提供了强大的模块化特性。它能帮助您编写所需的任意数量的适配器,而对软件其他部分的影响却很小。此外,由于领域与技术栈无关,因此可以更改技术栈而不会对业务造成任何影响。
- 始终从领域入手,专注于功能开发,就能确保为客户创造价值。这样一来,就可以推迟技术实现的选择,以便在合适的时机做出最佳选择。
一些反馈
六边形架构并非适用于所有情况。与领域驱动设计类似,它仅适用于拥有实际业务领域的应用。对于将数据转换为另一种格式的应用程序而言,它可能就显得过于复杂了。
最后,在采用新技术时,务必保持务实的态度。如前所述,Hexagon 架构不应依赖任何技术框架,但特殊情况下可以例外。例如,在我们的案例中,Hexagon 架构有三个例外:Apache Commons Lang3(StringUtils)、SLF4J 和 Findbugs 的 JSR305。因为我们不想重复造轮子,而且这些框架对领域的影响非常小。Hexagonal 架构的一个优点是,它促使你在集成新框架之前不断进行自我评估。通过使用这种架构,我们将领域依赖项的数量从五十个减少到仅三四个。从安全角度来看,这非常有利。
延伸阅读
想深入了解?不妨看看领域驱动设计和六边形架构技巧系列
文章 。您还可以在这里找到一篇关于六边形架构的精彩文章:https://softwarecampament.wordpress.com/portsadapters/
如果您想查看一些代码,现在可以在 GitLab 上找到一个 Kotlin/Spring Boot六边形应用程序。
法语版本:Architecture Hexagonale : le guide pratique pour une clean architecture .
文章《六边形架构:简洁架构的实用指南》最初发表于BeyondxScratch 网站。
文章来源:https://dev.to/julientopcu/hexagonal-architecture-the-practical-guide-for-a-clean-architecture-1j8n