端口与适配器架构
端口与适配器架构又被称为六边形架构,是一种约定代码设计的架构。它解决的是如何设计代码的问题,主要的关注点在于业务与技术的解耦。
第一次看到这个名字的时候,就有很多问题冒了出来:
它解决什么问题?
什么是端口?什么是适配器?
按照这个架构写出来的代码应该是什么样子?
它为什么又被称作六边形架构?有什么特殊含义?
带着这些问题,搜索了一些资料,算是有了下面的这些理解。
为什么又被称作六边形架构
其实它被称作六边形架构,仅仅是因为它的作者 Dr. Alistair Cockburn 画成这样的而已,没有什么深意。这个架构和“六”这个数字以及“六边形”这个图形都没有任何关联。
那么,这个“六边形”又代表什么呢?
这个“六边形”就是我们所说的“应用”,六边形内的东西,是与技术无关的业务代码。而这些业务代码如何设计,它并没有约定。你可以在内部使用 DDD
战术,也可以使用 “意大利面条式”的代码设计。
一些概念
Actor
Actor 是指那些与应用交互的组件,包括浏览器、命令行甚至其他的应用。所有这些东西都是在我们的“六边形”之外的。
Actor 也有分类,依据是与应用交互的方式:
Drivers
又被称作 Primary Actors。这些 Actor 的特点是它们会调用应用,以完成业务目的。
在画六边形时,Driver 会被画到六边形的左边和上面。
Driven Actors
又被称作 Secondary Actors。这些 Actor 被应用调用,它们提供技术方面的功能以帮助应用完成业务逻辑。
在画六边形时,Driven Actor 会被画到六边形的右边和下边。
Ports
端口处在“六边形”的边缘,用于应用和 Actors
的交互。
根据交互的 Actor
的不同,可以将端口分类为 Driver Port
和 Driven Port
,或者按照《微服务架构设计模式》的说法称作 入站端口
和 出站端口
。
它们被这样分类是因为:
入站端口提供了访问应用的
API
出站端口被应用调用,提供
SPI
供Driven Actor
实现
端口描述了应用的业务功能,关注点仍然在业务上。
以 Driver Port
来说,可以提供一个 createOrder
端口提供下单的功能。
以 Driven Port
来说,可以提供一个 sendNotification
端口来要求一个发送通知的功能。
但 Actor
并不是直接和端口交互,而是通过 Adapter
来进行的。
Adapters
适配器处在“六边形”之外,所有的 Actor
都是通过适配器来和 Port
交互,以达到和应用交互的效果。
适配器可以根据适配的端口的不同,分为 Driver Adapter
和 Driven Adapter
(或 入站适配器
和 出站适配器
)。
适配器就是从“六边形”中解耦出来的“技术”。
以 Driver Adapter
来说,可以实现 REST适配器
来提供 REST
风格的接口来调用端口使用应用;也可以实现 CLI适配器
来提供 CLI
接口来调用端口使用应用。
以 Driven Adapter
来说,可以实现一个 MySQL适配器
以使用 MySQL
持久化数据;也可以实现一个 MongoDB适配器
以使用 MongoDB
持久化数据;也可以实现一个 邮件通知适配器
来实现以邮件形式发送通知的功能。
小结
以上就是端口与适配器架构的核心的概念。
我们可以看到,它的关注点仅仅在于如何将技术代码与业务代码分离开。所以它定义了 Actors
、Ports
和 Adapters
这三个概念。而“六边形”并不是一个又特殊含义的东西,只有画出应用的边界和区别 Driver
与 Driven Actor
的作用。
这些概念中,只有 Ports
是属于应用的,其他两个概念都是在应用之外。
- 所有的业务代码,都被包含在了应用之中,而没有被泄漏到应用之外
- 所有需要与第三方交互的技术代码都被放到了 Adapters
里,也就是应用之外,没有侵入到业务代码中
解决的问题
与其他应用交互的代码的设计。
没解决的问题
业务代码怎么写。这需要使用其他的模式来解决,比如 DDD 的战术模式。
如何实现
端口与适配器架构模式要求将应用与适配器分离开,这意味着实现这个模式的时候,需要对应用代码和技术代码做一些技术隔离。
对于 Java
而言,无论是使用 Maven
还是 Gradle
都能比较容易的分离 subproject
。其他语言也可以使用类似的技术来实现。虽然不是特别麻烦,但总归是增加了系统的复杂度。
Java 的例子可以参考 这里。
总结
总的来说,端口与适配器模式并不复杂,搞清楚三个关键概念和“六边形”没有意义这个点,就算立即到这个架构模式了。