目录
一 软件架构的4+1模型
二 分层架构风格
三 六边型架构
四 代码示例
五 总结
一 软件架构的4+1模型
先上图,软件架构的4+1模型如图1.1所示:
图1.1 4+1模型
注:上图中的元素一词来源于软件架构的定义
计算机系统的软件架构是构建这个系统所需要的一组架构,包括软件元素、它们之间的关系以及两者的属性
-----Bass等著《Documenting Software Architectures:Views and Beyond》
每个视图的目的如下:
逻辑视图:以面向对象语言为例,元素包括类和包。它们之间的关系可理解为类和包之间的关系(继承、关联和依赖)。
实现视图:构建编译系统的输出。该视图由打包代码的模块和组件组成,组件是有一个或多个模块组成的可执行单元或可部署单元。元素之间的关系为模块之间的依赖或组合关系。
进程视图:表示运行时的组件。其中,元素表示一个进程,元素之间的关系为进程间的通信。
部署视图:表示进程如何映射到机器。其中,元素由机器和进程组成,机器之间的关系为网络。
4 + 1中的1表示场景,场景负责把视图串联在一起。每个场景负责描述在一个视图中的多个架构元素是如何协作来完成一个请求。
二 分层架构风格
分层架构是典型的架构例子,该架构将元素按照“层”的方式组织,每个层有定义明确的职责,同时限制了层与层之间的依赖关系。即:每一层只能依赖于紧邻的下层或下面的任何层。以常见的三层架构为例(三层架构是应用于逻辑视图的分层架构),三层架构将应用程序的类组织展示如图2.1所示:
图2.1三层分层架构
在实际开发过程中,三层架构随着业务的逐渐庞大会出现很明显的弊端,比如,在表示层中有逻辑层的代码,导致在实际的三层架构模型中,下层会依赖上层,违背了三层分层机构只能上层依赖下层的原则,导致分层边界越来越模糊。
三 六边型架构
为了解决分层架构,随着业务逐渐判断导致边界模糊的问题,六边形架构应运而生。六边形架构(也称为端口-适配器架构)可以看做是分层架构的替代品,如图3.1所示。
图3.1 六边形架构结构图
六边形架构风格以业务逻辑为核心的方式组织逻辑视图。应用具备一个或多个入站适配器,而不是表示层,入站适配器通过调用端口来调用业务逻辑处理外部的请求。同样的,应用具备一个或多个出站适配器,而不是数据持久层,出站适配器由业务逻辑中的出站端口调用,来调用外部系统(mysql,redis,zk等等)。六边形架构的一个优点是业务逻辑不依赖于适配器,而是适配器来依赖于业务逻辑。
业务逻辑具有一个或多个端口,在端口中,定义了一组操作,这些操作关于业务如何与外部交互(处理外部请求、调用外部系统)。以Java开发为例,端口可以理解为Java接口,入站端口是业务逻辑公开的API(如Controller调用的业务逻辑接口),供外界调用。出站端口是业务逻辑调用外部系统的方式。
业务逻辑周围有适配器,适配器也包括入站适配器和出站适配器。其中,入站适配器通过调用入站端口来处理外部的请求。我们常见的入站适配器有Spring MVC的controller,消息代理客户端等。特别注意的是,多个入站适配器可以调用相同的入站端口。
出站适配器实现出站端口,通过调用外部程序/服务/系统的方式来处理业务逻辑的请求。如访问数据库,调用远程RPC,发布订阅事件等等。
六边形架构的一个重要好处是,它将业务逻辑和适配器的逻辑分离开,和三层分层架构不同的是,业务逻辑不再依赖表示层或数据访问层。通过这种分离,对业务逻辑进行单独的测试变得更容易。六边形架构更准确地反映了现代应用程序的架构。外部请求通过多个适配器调用业务逻辑。业务逻辑也可以调用多个出站适配器,每个适配器调用不同的外部系统。
四 代码示例
以Tom Hombergs的代码为例进行部分说明。代码地址如下:
/thombergs/buckpal,代码结构如图4.1所示:
图4.1 代码结构
六边形架构的原则是业务逻辑不能依赖与适配器,而是适配器需要依赖与业务逻辑。以入站适配器为例,SendMoneyController作为一个入站适配器,它需要调用入站端口来调用业务逻辑实现具体的请求,这里的入站端口为sendMoney(SendMoneyCommand command)方法,如图4.2所示:
图4.2 SendMonryCotroller
针对出站适配器,需要采用依赖倒置的原则(高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,一句话总结就是面向接口编程)。
在业务逻辑sendMoney方法中,声明了2个出站端口loadAccountPort和updateAccountStatePort。出站端口LoadAccountPort,UpdateAccountStatePort是两个接口,由对应的出站适配器去实现,出站端口是定义在业务逻辑中的,根据依赖倒置原则,出站适配器必须依赖于对应的出站适配器,进而依赖于业务逻辑,而不是业务逻辑依赖于出站适配器。实现了LoadAccountPort和UpdateAccountStatePort出站端口的出站适配器如图4.3所示:
图4.3AccountPersistenceAdapter出站适配器
在业务逻辑sendMoney方法中,通过调用实现了出站端口的出站适配器的具体方法在完成业务逻辑对外部系统的调用,如图4.4所示
图4.4 sendMoney业务逻辑
五 总结
以下是个人理解,可能有误:
在我看来,当一个服务体量不大的时候,三层分层结构和六边形架构的区别不大,因为在体量不大的情况下,服务的输入端口和输出端口较少,可以简单的将整个服务看做三层分层架构。随着服务体量的逐渐扩大,在三层分层架构下,由于不同开发人员的编码习惯,难免会导致不同层次机构之间的互相侵入。此时六边形架构的特点会逐渐体现,通过依赖倒置,将业务逻辑和输入、输出隔离开,这也是六边形架构以业务为核心的特点。由于这种特性,六边形架构适用于对业务代码的测试,不用依赖于输入和外部系统。
还是那句话,技术没有银弹,选择什么样的技术,架构需要从自身的业务考虑,只有适合自己业务的架构,没有绝对一劳永逸的架构。
补充:仔细想了下,在传统的MVC中,service层中往往是依赖于出站的(调用第三方服务)。如果业务需要调用第三方依赖,传统的MVC会有局限性,运用六边形架构就能过将业务层和外部服务通过适配器剥离开。
如果觉得《微服务架构设计模式学习笔记——六边形架构》对你有帮助,请点赞、收藏,并留下你的观点哦!