11.端口和適配器架構(gòu)(譯)

原文:https://herbertograca.com/2017/09/14/ports-adapters-architecture/

這篇文章是軟件架構(gòu)編年史()的一部分燎潮,這部編年史由一系列關(guān)于軟件架構(gòu)的文章組成腥寇。在這一系列文章中嘿悬,我將寫下我對(duì)軟件架構(gòu)的學(xué)習(xí)和思考,以及我是如何運(yùn)用這些知識(shí)的棚品。如果你閱讀了這個(gè)系列中之前的文章,本篇文章的的內(nèi)容將更有意義。

2005年,Alistair Cockburn構(gòu)思了端口和適配器架構(gòu) (又稱六邊形架構(gòu))并記錄在他的博客中陨献。下面這句話就是他對(duì)該架構(gòu)的目標(biāo)的定義:

讓用戶、程序懂更、自動(dòng)化測(cè)試和批處理腳本可以平等地驅(qū)動(dòng)應(yīng)用,讓應(yīng)用的開(kāi)發(fā)和測(cè)試可以獨(dú)立于其最終運(yùn)行的設(shè)備和數(shù)據(jù)庫(kù)急膀【谛——Alistair Cockburn 2005,端口和適配器

有許多文章在談及端口和適配器架構(gòu)時(shí)會(huì)花很多篇幅在分層上卓嫂。然而慷暂, 我并沒(méi)有在 Alistair Cockburn 的原文中找到關(guān)于分層的只言片語(yǔ)。

其思想是將我們的應(yīng)用看作是一個(gè)系統(tǒng)的中心交付物晨雳,輸入和輸出都是通過(guò)端口出入應(yīng)用行瑞,這些端口將應(yīng)用和外部工具、技術(shù)以及傳達(dá)機(jī)制隔離開(kāi)來(lái)餐禁。應(yīng)用不應(yīng)該關(guān)心是誰(shuí)在發(fā)送輸入或接收輸出血久。這就是為了保護(hù)產(chǎn)品免受技術(shù)和業(yè)務(wù)需求演進(jìn)的影響。由于技術(shù)/供應(yīng)商鎖定帮非,這些演進(jìn)可能導(dǎo)致產(chǎn)品剛開(kāi)發(fā)沒(méi)多久就被廢棄氧吐。

我將在本文中剖析以下主題:

  • 傳統(tǒng)架構(gòu)方式的問(wèn)題
  • 分層架構(gòu)的演化
    • 什么是端口?
    • 什么是適配器末盔?
    • 適配器的兩種不同類型
  • 端口和適配器架構(gòu)有哪些優(yōu)勢(shì)?
    • 實(shí)現(xiàn)隔離和技術(shù)隔離
    • 傳達(dá)機(jī)制的隔離
    • 測(cè)試
  • 總結(jié)

傳統(tǒng)架構(gòu)方式的問(wèn)題

傳統(tǒng)的架構(gòu)方式在前端和后端都可能給我們帶來(lái)問(wèn)題筑舅。

在前端,業(yè)務(wù)邏輯最終可能會(huì)滲透到 UI(例如陨舱,我們把用例的邏輯放到控制器或視圖里翠拣,導(dǎo)致這些邏輯不能在其它 UI 界面中重用), 甚至 UI 會(huì)反過(guò)來(lái)滲透到業(yè)務(wù)邏輯中(例如游盲,我們會(huì)為了模板中需要的業(yè)務(wù)邏輯在實(shí)體中創(chuàng)建對(duì)應(yīng)的方法)误墓。

而在后端蛮粮,我們可能會(huì)在自己的業(yè)務(wù)邏輯里使用外部類的類型提示、繼承或者實(shí)例化它們优烧,這會(huì)導(dǎo)致對(duì)這些外部的庫(kù)和技術(shù)直接引用蝉揍,最后任由它們滲透到業(yè)務(wù)邏輯中。

分層架構(gòu)的演化

EBI ()和DDD()的福, 2005 年我們已經(jīng)知道了“系統(tǒng)中真正重要的是位于中間的層次”畦娄。業(yè)務(wù)邏輯(應(yīng)該)存在于這些層次之中又沾,它們才是我們和競(jìng)品的真正區(qū)別。這才是真正的“應(yīng)用”熙卡。


但是杖刷,Alistair Cockburn 意識(shí)到 頂部和底部的層次從另一方面來(lái)說(shuō),就是應(yīng)用的入口/出口驳癌。盡管實(shí)際中它們不一樣滑燃,卻有著十分相似的目標(biāo),在設(shè)計(jì)上也是對(duì)稱的颓鲜。而且表窘,如果我們想要隔離出應(yīng)用中間的層次,這些入口和出口能以另一種相似的方式使用甜滨。

區(qū)別于典型的分層架構(gòu)圖乐严,我們將它們畫在系統(tǒng)的左右兩側(cè),而不是上下兩邊衣摩。

雖然我們識(shí)別出了系統(tǒng)中對(duì)稱的兩側(cè)昂验,但兩側(cè)都可能有若干入口/出口。例如艾扮, API和UI就是位于應(yīng)用左側(cè)的兩個(gè)不同的入口/出口既琴。為了表示應(yīng)用有若干個(gè)入口/出口,我們把應(yīng)用的形狀改成了多邊形泡嘴。應(yīng)用的形狀可以是有多條邊的任意多邊形甫恩,但最終六邊形獲得了青睞。這也是“六邊形架構(gòu)”的由來(lái)磕诊。

端口和適配器架構(gòu)使用了實(shí)現(xiàn)為端口和適配器的抽象層次填物,解決了傳統(tǒng)架構(gòu)方式帶來(lái)的問(wèn)題。

什么是端口霎终?

端口是對(duì)其消費(fèi)者無(wú)感知的進(jìn)入/離開(kāi)應(yīng)用的入口和出口滞磺。在許多編程語(yǔ)言里,端口就是接口莱褒。例如击困,在搜索引擎里它可能是執(zhí)行搜索的接口。在應(yīng)用中,我們把這個(gè)接口當(dāng)成入口/出口使用阅茶,而不用去關(guān)心它的具體實(shí)現(xiàn)蛛枚,實(shí)際上在所有將接口定義為類型提示的地方,這些實(shí)現(xiàn)會(huì)被注入脸哀。

什么是適配器蹦浦?

適配器是將一個(gè)接口轉(zhuǎn)換(適配)成另一個(gè)接口的類。

例如撞蜂,一個(gè)適配器實(shí)現(xiàn)了接口 A 并被注入了接口 B盲镶。當(dāng)這個(gè)適配器被實(shí)例化時(shí),一個(gè)實(shí)現(xiàn)了接口B的對(duì)象將從構(gòu)造方法注入進(jìn)來(lái)蝌诡。實(shí)現(xiàn)了接口 A 的 對(duì)象會(huì)被注入到需要接口A的地方溉贿,然后接收方法請(qǐng)求,將其轉(zhuǎn)換并代理給那個(gè)實(shí)現(xiàn)了接口B的內(nèi)部對(duì)象浦旱。

如果我說(shuō)的不夠明白宇色,別慌,后面我會(huì)給出一個(gè)更具體的例子颁湖。

適配器的兩種不同類型

左側(cè)代表 UI 的適配器被稱為主適配器或者主動(dòng)適配器宣蠕,因?yàn)槭撬鼈儼l(fā)起了對(duì)應(yīng)用的一些操作。而右側(cè)表示和后端工具鏈接的適配器甥捺,被稱為從適配器或者被動(dòng)適配器植影,因?yàn)樗鼈冎粫?huì)對(duì)主適配器的操作作出響應(yīng)。

端口/適配器的用法也有一點(diǎn)區(qū)別:

  • 左側(cè)涎永,適配器依賴端口,該端口的具體實(shí)現(xiàn)會(huì)被注入到適配器鹿响,這個(gè)實(shí)現(xiàn)包含了用例羡微。換句話說(shuō),端口和它的具體實(shí)現(xiàn)(用例)都在應(yīng)用內(nèi)部惶我。
  • 右側(cè)妈倔,適配器就是端口的具體實(shí)現(xiàn),它自己將被注入到我們的業(yè)務(wù)邏輯中绸贡,盡管業(yè)務(wù)邏輯只知道接口盯蝴。換句話說(shuō),端口在應(yīng)用內(nèi)部听怕,而它的具體實(shí)現(xiàn)在應(yīng)用之外并包裝了某個(gè)外部工具捧挺。

端口和適配器架構(gòu)有哪些優(yōu)勢(shì)?

使用這種應(yīng)用位于系統(tǒng)中心的端口/適配器設(shè)計(jì)尿瞭,讓我們可以保持應(yīng)用和實(shí)現(xiàn)細(xì)節(jié)之間的隔離闽烙,這些實(shí)現(xiàn)細(xì)節(jié)包括曇花一現(xiàn)的技術(shù)、工具和傳達(dá)機(jī)制声搁。它還讓可重用的概念更容易更快速地得到驗(yàn)證并被創(chuàng)建出來(lái)黑竞。

實(shí)現(xiàn)隔離和技術(shù)隔離

上下文

我們的應(yīng)用使用SOLR作為搜索引擎捕发,并使用一個(gè)開(kāi)源庫(kù)連接它并執(zhí)行搜索。

傳統(tǒng)架構(gòu)方式

傳統(tǒng)架構(gòu)方式下很魂,我們會(huì)直接在我們的代碼中使用庫(kù)(SOLR)里的類扎酷,作為類型提示,或者實(shí)例化和/或作為我們實(shí)現(xiàn)的基類遏匆。

端口和適配器架構(gòu)方式

如果采用端口和適配器架構(gòu)的話法挨,我們會(huì)創(chuàng)建一個(gè)接口,比如叫做 UserSearchInterface拉岁,在代碼中用這個(gè)接口作為類型提示坷剧。我們還會(huì)為 SOLR 創(chuàng)建一個(gè)實(shí)現(xiàn)該接口的適配器,比如叫做 UserSearchSolrAdapter喊暖。這個(gè)實(shí)現(xiàn)是 SOLR 的包裝惫企,SOLR 會(huì)被注入其中并用來(lái)實(shí)現(xiàn)接口指定的方法。

問(wèn)題

不久之后陵叽,我們想用Elasticsearch換掉SOLR狞尔。甚至,對(duì)于同樣的搜索行為巩掺,我們希望有些時(shí)候使用SOLR偏序,有些時(shí)候使用Elasticsearch,在運(yùn)行時(shí)決定就好胖替。

如果我們采用傳統(tǒng)架構(gòu)研儒,我們需要查找所有使用SOLR的代碼并替換成Elasticsearch。然而独令,這可不是簡(jiǎn)單的查找替換:兩個(gè)引擎的用法不同端朵,方法、輸入燃箭、輸出也不盡相同冲呢,替換并不是一件輕松的任務(wù)。而在運(yùn)行時(shí)在決定使用那個(gè)引擎甚至是不可能的招狸。

然而敬拓,假設(shè)我們使用了端口和適配器架構(gòu),我們只需要?jiǎng)?chuàng)建一個(gè)新的適配器裙戏,比如就叫UserSearchElasticsearchAdapter乘凸,在注入時(shí)使用它換掉SOLR的適配器,也許改一下DCI中的配置就可以做到挽懦。我們完全可以使用工廠來(lái)決定注入那個(gè)適配器翰意,實(shí)現(xiàn)在運(yùn)行時(shí)注入不同的實(shí)現(xiàn)。

傳達(dá)機(jī)制的隔離

和上面這個(gè)例子類似,假設(shè)我們的應(yīng)用需要 Web GUI冀偶,CLI 和 Web API醒第。我們想在全部三種 UI 中提供某個(gè)功能,比如叫做UserProfileUpdate的功能进鸠。

使用端口和適配器架構(gòu)的話稠曼,我們會(huì)在一個(gè)應(yīng)用服務(wù)的方法中實(shí)現(xiàn)這個(gè)功能并將其作為一個(gè)用例。服務(wù)會(huì)實(shí)現(xiàn)一個(gè)接口客年,該接口說(shuō)明了方法霞幅、輸入以及輸出。

每個(gè)版本的 UI 都有各自的控制器(或控制臺(tái)命令)來(lái)通過(guò)這個(gè)接口觸發(fā)期望的邏輯量瓜,應(yīng)用服務(wù)接口的具體實(shí)現(xiàn)會(huì)被注入到 UI 中司恳。這種情況下,適配器實(shí)際上就是控制器(或 CLI 命令)绍傲。

之后我們可以修改 UI扔傅,因?yàn)槲覀冎肋@些修改不會(huì)影響業(yè)務(wù)邏輯。

測(cè)試

上面兩個(gè)例子中烫饼,使用端口和適配器架構(gòu)會(huì)讓測(cè)試更加容易猎塞。第一個(gè)例子中,我們用接口(端口)的 Mock 就可以測(cè)試應(yīng)用杠纵,而不需要使用 SOLR 或 Elasticsearch 荠耽。

第二個(gè)例子中,所有的 UI 都可以獨(dú)立于應(yīng)用進(jìn)行測(cè)試比藻。我們的用例也可以獨(dú)立于 UI 進(jìn)行測(cè)試铝量,傳給服務(wù)一些輸入再斷言結(jié)果就好。

總結(jié)

在我看來(lái)银亲,端口和適配器架構(gòu)只有一個(gè)目標(biāo):將業(yè)務(wù)邏輯和系統(tǒng)使用的傳達(dá)機(jī)制以及工具隔離款违。為此,它使用了常見(jiàn)的編程語(yǔ)言結(jié)構(gòu):接口群凶。

UI側(cè)(主動(dòng)適配器),我們創(chuàng)建使用應(yīng)用接口的適配器哄辣,比如控制器请梢。
基礎(chǔ)設(shè)施側(cè)(被動(dòng)適配器),我們創(chuàng)建實(shí)現(xiàn)應(yīng)用接口的適配器力穗,比如資源庫(kù)毅弧。

這就是全部!

然而当窗,我驚訝的發(fā)現(xiàn)早在十三年前同樣的思想就已經(jīng)公開(kāi)發(fā)表了()够坐,盡管它沒(méi)有刻意地強(qiáng)調(diào)要將工具和傳達(dá)機(jī)制從應(yīng)用核心中隔離出來(lái)。

系統(tǒng)和角色的任何交互都要通過(guò)邊界對(duì)象。按照 Jacobson 的描述元咙,角色可以是客戶或者管理員(操作員)這樣的人類用戶梯影,也可以是定時(shí)器或者打印機(jī)這樣的非人類“用戶”,它們分別對(duì)應(yīng)著端口和適配器架構(gòu)中的主動(dòng)適配器被動(dòng)適配器庶香。

引用來(lái)源

1992 – Ivar Jacobson – Object-Oriented Software Engineering: A use case driven approach
200? – Alistair Cockburn – Hexagonal Architecture
2005 – Alistair Cockburn – Ports and Adapters
2012 – Benjamin Eberlei – OOP Business Applications: Entity, Boundary, Interactor
2014 – Fideloper – Hexagonal Architecture
2014 – Philip Brown – What is Hexagonal Architecture?
2014 – Jan Stenberg – Exploring the Hexagonal Architecture
2017 – Grzegorz Ziemoński – Hexagonal Architecture Is Powerful
2017 – Shamik Mitra – Hello, Hexagonal Architecture

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末甲棍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赶掖,更是在濱河造成了極大的恐慌感猛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奢赂,死亡現(xiàn)場(chǎng)離奇詭異陪白,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)膳灶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門咱士,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人袖瞻,你說(shuō)我怎么就攤上這事司致。” “怎么了聋迎?”我有些...
    開(kāi)封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵脂矫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我霉晕,道長(zhǎng)庭再,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任牺堰,我火速辦了婚禮拄轻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伟葫。我一直安慰自己恨搓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布筏养。 她就那樣靜靜地躺著斧抱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渐溶。 梳的紋絲不亂的頭發(fā)上辉浦,一...
    開(kāi)封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音茎辐,去河邊找鬼宪郊。 笑死掂恕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弛槐。 我是一名探鬼主播懊亡,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼丐黄!你這毒婦竟也來(lái)了斋配?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤灌闺,失蹤者是張志新(化名)和其女友劉穎艰争,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體桂对,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡甩卓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蕉斜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逾柿。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宅此,靈堂內(nèi)的尸體忽然破棺而出机错,到底是詐尸還是另有隱情,我是刑警寧澤父腕,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布弱匪,位于F島的核電站,受9級(jí)特大地震影響璧亮,放射性物質(zhì)發(fā)生泄漏萧诫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一枝嘶、第九天 我趴在偏房一處隱蔽的房頂上張望帘饶。 院中可真熱鬧,春花似錦群扶、人聲如沸及刻。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)提茁。三九已至,卻和暖如春馁菜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铃岔。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工汪疮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峭火,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓智嚷,卻偏偏與公主長(zhǎng)得像卖丸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盏道,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理稍浆,服務(wù)發(fā)現(xiàn),斷路器猜嘱,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,510評(píng)論 25 707
  • 用兩張圖告訴你衅枫,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,693評(píng)論 2 59
  • 一個(gè)肥胖的渾身臟兮兮的女人朗伶,胡亂穿了幾層色彩鮮艷的衣服弦撩,黑色的頭發(fā)像瘋長(zhǎng)的野草似的拱滿了灰塵。 她出門不是來(lái)醫(yī)院...
    豌豆P閱讀 121評(píng)論 0 0
  • 差單反论皆,破鏡頭益楼,但是我愛(ài)生活……
    老陶有故事閱讀 277評(píng)論 1 3