六邊形架構(端口與適配器)
在六邊形架構[2]中,Alistair Cockburn提出了一種具有對稱性特征的架構風格[Cockburn]。在這種架構中,不同的客戶通過“平等”的方式與系統交互。需要新的客戶嗎?不是問題。只需要添加一個新的適配器將客戶輸入轉化成能被系統API所理解的參數就行了。同時,系統輸出,比如圖形界面、持久化和消息等都可以通過不同方式實現,并且是可互換的。這是可能的,因為對于每種特定的輸出,都有一個新建的適配器負責完成相應的轉化功能。
至此,我們有充足的理由認為,這將是一種具有持久生命力的架構。
現在,很多聲稱使用分層架構的團隊實際上使用的是六邊形架構。這是因為很多項目都使用了某種形式的依賴注入。并不是說依賴注入天生就是六邊形架構,而是說使用依賴注入的架構自然地具有了端口與適配器風格。我們將對此做詳盡的解釋。
我們通常將客戶與系統交互的地方稱為“前端”;同樣,我們將系統中獲取、存儲持久化數據和發送輸出數據的地方稱為“后端”。但是,六邊形架構提倡用一種新的視角來看待整個系統,如圖4.4所示。該架構中存在兩個區域,分別是“外部區域”和“內部區域”。在外部區域中,不同的客戶均可以提交輸入;而內部的系統則用于獲取持久化數據,并對程序輸出進行存儲(比如數據庫),或者在中途將輸出轉發到另外的地方(比如消息)。
牛仔的邏輯
AJ:“我的那些馬確實很喜歡它們的六邊形蓄欄,因為當我帶上馬鞍要騎它們時,它們有更多的角落可以跑躲。”

在圖4.4中,每種類型的客戶都有它自己的適配器[Gamma et al.],該適配器用于將客戶輸入轉化為程序內部API所能理解的輸入。六邊形每條不同的邊代表了不同種類型的端口,端口要么處理輸入,要么處理輸出。圖4.4中有3個客戶請求均抵達相同的輸入端口(適配器A、B和C),另一個客戶請求使用了適配器D。可能前3個請求使用了HTTP協議(瀏覽器、REST和SOAP等),而后一個請求使用了AMQP協議(比如RabbitMQ)。端口并沒有明確的定義,它是一個非常靈活的概念。無論采用哪種方式對端口進行劃分,當客戶請求到達時,都應該有相應的適配器對輸入進行轉化,然后端口將調用應用程序的某個操作或者向應用程序發送一個事件,控制權由此交給內部區域。

圖4.4 六邊形架構也稱為端口與適配器。對于每種外界類型,都有一個適配器與之相對應。外界通過應用層API與內部進行交互。
我們不必自己實現端口
通常來說,我們都不用自己實現端口。我們可以將端口想成是HTTP,而將適配器想成是Java的Servlet或JAX-RS的REST請求處理類。或者,我們可以為NServiceBus或RabbitMQ創建消息監聽器,在這種情況下,端口是消息機制,而適配器則是消息監聽器,因為消息監聽器將負責從消息中提取數據,并將數據轉化為應用層API(領域模型的客戶)所需的參數。
按照功能需求來設計內部區域中的應用程序
在使用六邊形架構時,我們應該根據用例來設計應用程序,而不是根據需要支持的客戶數目來設計。任何客戶都可能向不同的端口發出請求,但是所有的適配器都將使用相同的API。
應用程序通過公共API接收客戶請求。應用程序邊界,即內部六邊形,也是用例(或用戶故事)邊界。換句話說,我們應該根據應用程序的功能需求來創建用例,而不是客戶數量或輸出機制。當應用程序通過API接收到請求時,它將使用領域模型來處理請求,其中便包括對業務邏輯的執行。因此,應用層API通過應用服務的方式展現給外部。再次提醒大家,這里的應用服務是領域模型的直接客戶,就像在分層架構中一樣。
以下代碼表示通過JAX-RS發布的RESTful資源。當請求到達HTTP的輸入端口時,相應的適配器將對請求的處理委派給應用服務:

JAX-RS所提供的Java注解構成了適配器的大部分功能,它們負責解析資源路徑,并將資源參數轉化為String類型參數。ProductService實例是注入進來的,請求便是通過該ProductService將處理委派到應用程序內部的。之后,Product對象將被序列化成XML,然后放在Response中,再由HTTP輸出端口發出。
JAX-RS并不是我們的關注點
JAX-RS只是使用應用程序和領域模型的一種方式。在這里,JAX-RS并不重要,我們完全可以使用Restfulie或者Node.js來完成相同的功能。但不管采用哪種方式,不同的適配器都會將輸入委派給相同的API。.
對于圖4.4中右側的端口和適配器,我們應該如何看待呢?我們可以將資源庫的實現看作是持久化適配器,該適配器用于訪問先前存儲的聚合實例,或者保存新的聚合實例。正如圖中的適配器E、F和G所展示的,我們可以通過不同的方式實現資源庫,比如關系型數據庫、基于文檔的存儲、分布式緩存和內存存儲等。如果應用程序向外界發送領域事件消息,我們將使用適配器H進行處理。該適配器處理消息輸出,而剛才提到的處理AMQP消息的適配器則是處理消息輸入的,因此應該使用不同的端口。
六邊形架構的一大好處在于,我們可以輕易地開發用于測試的適配器。整個應用程序和領域模型可以在沒有客戶和存儲機制的條件下進行設計開發。在測試時,我們可以方便地對ProductService進行替換,而無須考慮它是應該支持HTTP/REST呢,還是SOAP呢,或者是消息端口。任何測試客戶都可以在用戶界面還未完成之前進行開發。在選擇持久化機制之前,我們可以在測試中采用內存資源庫來模擬持久化。更多的內存持久化實現細節,請參考資源庫(12)。如此一來,我們可以在核心領域上進行持續開發,而不需要考慮那些支撐性的技術組件。
如果你采用的是嚴格分層架構,那么你應該考慮推平這種架構,然后開始采用端口與適配器。如果設計得當,內部六邊形——也即應用程序和領域模型——是不會泄漏到外部區域的,這樣也有助于形成一種清晰的應用程序邊界。在外部區域,不同的適配器可以支持自動化測試和真實的客戶請求,還有存儲、消息和其他輸出機制等。
當SaaSOvation的開發團隊考慮了六邊形架構的優點之后,他們決定從分層架構轉向六邊形架構。事實上,這并不困難,只是需要從不同的角度來看待和使用Spring框架而已。

六邊形架構的功能如此強大,以致于它可以用來支持系統中的其他架構。比如,我們可能采用SOA架構、REST或者事件驅動架構;也有可能采用CQRS;或者數據網織或基于網格的分布式緩存;還有可能采用Map-Reduce這種分布式并行處理方式。以上這些架構我們會在后續章節中講到。六邊形架構為這些架構提供了堅實的支撐基礎。當然,能夠提供這種基礎的不只是六邊形架構,但是在本章剩下的內容中,我們都假設使用這種架構。
- 數據產品經理高效學習手冊:產品設計、技術常識與機器學習
- Python數據分析入門:從數據獲取到可視化
- 計算機信息技術基礎實驗與習題
- 正則表達式必知必會
- Game Development with Swift
- MongoDB管理與開發精要
- Oracle RAC 11g實戰指南
- 醫療大數據挖掘與可視化
- “互聯網+”時代立體化計算機組
- Flutter Projects
- 數據庫應用系統開發實例
- SIEMENS數控技術應用工程師:SINUMERIK 840D-810D數控系統功能應用與維修調整教程
- R Machine Learning Essentials
- Hands-On Deep Learning for Games
- Microsoft Dynamics NAV 2015 Professional Reporting