在我工作过的一家公司中,我的团队被要求将旧应用程序移植到全新的堆栈上(例如从EAR / SQL应用程序迁移到独立的/ NoSQL应用程序)。通过研究,我们很快意识到我们必须重做整个基础架构……新框架与十年前所使用的框架有很大不同。实际上,唯一不需要更改的就是业务逻辑。因此重用它是有意义的,对吧?
经过更深入的研究,名为model的Maven模块只是POJO,完全是 贫血的 …尽管还有服务模块,但是业务逻辑在所有层之间都是共享的,淹没在许多技术代码(DAO创建,序列化,池管理)中等)。业务的某些部分依赖于我们尝试删除的旧框架的技术。在技术代码和业务逻辑之间没有明确的区分。
关注点分离
我们寻求一种方法来实现这种分离,即如果有一天我们必须再次更改技术堆栈,则可以完全重用业务逻辑。我们的一位同事向我们介绍了 六角形架构 。该体系结构的关键概念之一是将所有业务模型/逻辑放在一个地方。因此,如果我回顾以前的架构,我们将得到以下内容:
另一个关键概念是领域只依赖于自身 ; 这是确保业务逻辑与技术层分离的唯一方法。我们如何在先前的架构上实现这一目标?领域显然取决于持久层!
通过使用一种模式:控制反转IOC。如果我们以此来重做以前的模式,并将领域称为Hexagon,因为Alistair Cockburn(HexArch的创建者)喜欢这种六边形形状:
好的,控制反转非常神奇,稍后我们将看到其工作原理。但是现在您了解了什么是六角结构:
让我为您解释最后一点,这是非常重要的一点。在另一个经验中,我的团队不得不将应用程序从“传统” Spring框架移植到Spring Boot。Spring Boot为您简化了很多事情;这意味着它有时可能与Spring框架有很大的不同。我们遇到的主要(痛苦)问题是,我们在Spring Integration Tests中大量利用其来验证我们的功能,而这些功能在很大程度上依赖于Spring。而且因为我们没有意识到Spring Boot与我们正在使用的其他框架有特殊的集成,所以我们第一次集成它时,所有这些测试都失败了。我们无法说出我们是在某个地方破坏了业务逻辑还是源于纯粹的技术问题。
通过确保六边形不依赖任何框架,即使您决定更改堆栈,也可以确保您的业务域可以重用。您还将增加域的可测试性,因为您不再将其与集成问题混在一起,因此您将进行实际的功能测试。这样,这些测试将直接与六边形交互,并且仅与六边形交互。我稍后再讨论。
六边形魔术
还记得控制权的倒置吗?为了确保六边形的隔离,对下游层的依赖性已被反转。如您所见,该技巧实际上非常简单:
六边形(基础结构)的外部分为两个虚拟部分,左侧和右侧。在左侧,您可以查询域的所有内容(控制器,REST层等),在右侧,您可以为域提供一些信息/服务的所有内容(持久层,第三方服务等) )。为了让外部与域进行交互,Hexagon提供了分为两类的业务接口:
这里有两个重要事实:
在经典的分层架构中,业务对象或服务通常会创建DAO实现持久层。在六边形中,域仅处理域对象。持久层负责将域对象转换为要持久化的任何技术对象(通过DAO),如带注释的JPA POJO或其他方式)。
你能感觉到力量吗?
六边形体系结构也称为端口和适配器体系结构。它来自于此体系结构的模块化功能。因为一切都是分离的,所以您可以在领域的前面同时拥有一个SOAP,REST和JMS层,而不会对其产生任何影响。在SPI方面,如果需要,您可以从MongoDB驱动程序实现更改为Cassandra。由于更改持久性模块不会更改SPI,因此其余软件不会受到影响。API和SPI是端口,使用或实现它们的基础结构模块是适配器。
如何执行呢?
这里还有一个规则:总是从六边形的内部开始。这将为您带来很多好处:
我的建议是首先以 BDD / ATDD方式编写您的功能/验收方案。然后编写API接口,它将成为功能的入口点。实施您的测试(赢取TDD!)以及实施您的业务逻辑之后。您可能需要定义一个SPI(例如从数据库中检索一些数据)就可以了。并且由于尚未实现右侧,因此请在六边形内部创建SPI的存根实现(例如,使用HashMap的内存数据库)。
您可以选择将存根实现保留在应用程序的测试范围内,也可以根据需要临时提供。例如,一旦我们在六边形上实现了第一个功能,就需要对外部第三方服务和数据库进行存根。因为我们的客户需要我们提供接口合同,所以我们接下来通过REST控制器导出了域。因此,我们在基础架构的右侧发布了带有存根数据的第一个版本,但是客户端能够看到我们的数据结构以及该功能的预期行为。而且,与手动创建功能的输入和输出的一些JSON示例相比,它要可靠得多,因为它实际上处理了实际的业务约束。
下一步通常是先打开左侧。这样,您可以对功能进行一些集成测试。目前,您可以提供 一些实时文档, 并确保与客户的接口合同。
最后,通过利用您进行的集成测试来实现功能的SPI,在右侧打开。我强烈建议您的测试是独立的,以避免在构建期间出现任何不稳定情况。您应该始终使用 Wiremock 等外部服务或 Fongo 模拟MongoDB来模拟第三方。
为您的其他功能循环同样的方法。
有关更多信息,可在GitLab上获取 六角结构测试策略 。
总结的六边形架构
将业务逻辑与技术代码脱钩确实有好处。就技术的不断发展而言,它确保您的业务领域持久耐用。
六边形形体系结构为您提供了一种实现此目标的真正方法:
六边形结构不应在所有情况下使用。就像DDD(并且六边形与之非常匹配)一样,如果您拥有真正的业务领域,则这确实适用。对于将数据转换为另一种格式的应用程序来说,这可能是过大的选择。
为此,在采用新技术时始终要务实。如前所述, 六边形一定不能依赖任何技术框架 ,但是您可以例外。例如,在我们的案例中,六边形有三个例外:Apache Commons Lang3(StringUtils),SLF4J和Findbugs的JSR305。因为我们不想再制造轮子,所以我们认为那些框架对领域的影响很小。六边形的一个很好的副作用是您在集成新框架之前一直挑战自己。在六边形之前,我们已经获得了该域的大约五十个依赖关系,并且已将其减少到其中的三个或四个。从安全角度来看,这是非常好的。
想看一些代码吗?在 GitLab 上检出使用六角结构构建的Kotlin / SpringBoot演示应用程序。