“純天然”的微服務架構

從分層架構到六邊形架構

在計算機領域很多概念都是抽象的,爲了能夠清晰地劃分這些抽象的複雜概念,隨處可見各種分層式的設計方式。比如計算網路的七層通信協議,把複雜的網絡通信劃分成更容易管理的層,明確定義各層的職責範圍,各層都可以採用最合適的技術來實現。當任何一層發生變化時,只要層間接口關係保持不變,則在這層以上或以下各層均不受影響。

在常見的軟件系統的架構中也有一種4層架構設計,通常從前到後會劃成:用戶接口層,應用層,領域層,基礎設施層。


  • 用戶接口層只需要關注如何同使用者打交道,不需要關注核心的業務邏輯,例如把用戶提交的數據經過轉碼處理後提供給應用層。
  • 應用層的關注點是如何組裝核心的領域層來提供複雜的組合服務,例如應用層可以從領域層的用戶倉庫獲取用戶信息,從領域層的訂單倉庫獲取訂單信息,然後在應用層把用戶信息和訂單信息組裝在一起返回給用戶接口層
  • 領域層處理的業務問題,是更加內聚的領域業務,不同業務之間要做到低耦合。例如在用戶聚合中就只處理用戶相關的邏輯,不應該包含訂單,支付這些其它的業務邏輯。
  • 基礎設施層是不包含任何業務邏輯的底層支持,例如生成pdf或者excel,或者把數據持久化到數據庫的ORM框架

但是這樣的分層架構會導致其他三層尤其是領域層對基礎設施層產生依賴。這種依賴會導致當基礎設施層發生變更的時候,會導致其它三層也要跟隨變動,這是我們不希望放生的事情,尤其是涉及核心業務邏輯的領域層,我們不希望因爲某種生成excel或者pdf的技術發生變化而要修改領域層。

那麼如果考慮對基礎設施進行解耦的方法,可供選擇的的方式有依賴倒置,在每一層定義對應的技術接口,然後在基礎設施層實現這個接口,再通過一些依賴注入框架在運行式把實現注入進來,這樣就把原來其它三層依賴基礎設施層的情況倒置成了基礎設施層依賴其它三層了。

有趣的是,當我們在分層架構中採用依賴導致原則時,我們會發現事實上已經不存在分層的概念了。無論高層還是低層,都只依賴於抽象的接口。如果我們這時把分層架構推平,這就是在微服務領域非常著名的六邊形架構

六邊形架構是Alistair Cockburn提出的,其實是把原來的分層架構轉換了一下視角,從原來的上下結構變成了內外結構。六邊形架構把原來一個系統的輸入和輸出都統一來處理,輸入輸出對接的都是外部系統,都需要一個適配器來進行轉換。所以不論是處理輸入的HTTP請求,還是處理輸出的數據庫存儲都應該有一個適配器來負責接入。適配器會調用負責業務邏輯組合的應用層,而應用層負責調用核心的領域層。

相比於應用層,領域層通常更少變化。在這樣的架構關係下,當需要修改一個負責連接數據庫的ORM工具時,或者是應用層返回的訂單信息需要增加物流狀態時,都不需要去修改最核心的領域層。

爲什麼是六邊形

不知道大家在看到六邊形架構這個名字的時候會不會產生一個疑問,就說是爲什麼是六邊形,不是七邊形,五邊形。

這個問題其實困擾了我蠻久,但是當我在鄭也夫老師的社會學專題50講裏面找到了答案。六邊形這個概念源自於城市規劃,有兩個德國人,一個是克里斯塔勒,他的研究興趣是人口密度和商品銷售關係。他1933年寫出一部書,叫做《德國南部中心地原理》。另外一個是廖什 ,他的興趣在於研究空間秩序,他在1940年寫出一部書叫做《經濟空間秩序》。

這兩個人各自獨立的提出了六邊形理論

每個商家銷售的範圍是一個圓。因爲人們買東西有交通成本,所以導致着以一個賣點爲中心,銷售範圍是圍繞着這個中心畫的一個圓。當人口越來越多的時候,商家也就越來越多,那個圓形越來越多,導致着一個圓形跟另一個圓形相互擠壓,最後壓成了一個蜂巢一樣的緊密相連的正六邊形的組合體。

最低級的銷售點是集市,銷售一些最通常的日用品,構成了最小的六邊形拼湊出來的平面。稍大一點的城市會賣衣服、鞋等等中檔的東西,它們就是中型的六邊形。大的都市會賣一些很高檔的東西,珠寶、珍貴的皮毛等等。這些大的正六邊形拼湊出一個大的平面。並且在一個廣大的地域中,大都市銷售區是大六邊形的對接,其中包含着中小六邊形。

除了人類的集市,還有一種自然界的天然六邊形就是蜂巢。但是你有想過這個問題嗎,蜜蜂在搭建蜂巢的時候又沒有測量工具,它們是怎麼搭建出來比例那麼標準的的六邊形蜂巢的呢。
其實蜜蜂根本就不曾想過要搭建六邊形,蜜蜂在搭建蜂巢的時候是按照記得身體的大小圍出了圓形的一個個蜂巢,請看下圖:


然後這一個個小圓經過蜂巢自己本身的拉伸和風化,就變成了六邊形。蜜蜂本來就是要造圓形巢的,但是由於自然界本身的趨於穩定的焓變規則,軟的圓形巢就拉成六邊形了。
[圖片上傳失敗...(image-750f2e-1541523534115)]

那麼爲什麼圓形會被拉伸成六邊形呢?阿爾法小分隊有一個視頻專門介紹了這個問題的數學原理(視頻傳送門)。在視頻中用小時候經常玩的泡泡做了一個簡單的實驗,當四個泡泡湊仔一起的的時候,在泡泡邊界上出現的所有夾角都是120度。所以當很多個泡泡擠在一起的時候,由於在拉扯力的作用下邊界夾角都是120度,所以一個個泡泡就“天然的”變成了六邊形。而蜂巢,人類集市是遵循這個自然規律。

回到微服務架構,雖然“六邊形架構”這個概念是Alistair Cockburn創造出來的,但是由於六邊形代表了自然規律,所以我認爲六邊形架構是“純天然”的。而且如果我們把創建的微服務看成一個個小泡泡,那麼把這些小泡泡排列在一起以後,自然法則會讓他們變成一個個六邊形。而不可能是五變形或者七邊形。

介紹完了六邊形的故事,讓我們進入六邊形架構的微服務內部,看看裏面的代碼是什麼樣子的。

一個六邊形架構的代碼結構示例

首先在一個微服務的根目錄上應該只有3個包,adapter,application和domain。而且還要嚴格限制依賴關係:

  • domain中不可以依賴任何application和adapter中的代碼
  • application中不可以依賴任何adapter中的代碼

假設一個簡單的場景:用戶從界面提交一個支付請求,最終這個支付請求的相關信息會被存入數據庫中。

接受請求這段很好實現,在adapter層裏有一個controller來處理請求,把請轉換成領域模型payment,然後調用application層SubmitPaymentApplication的submit方法來提交payment。然後SubmitPaymentApplication會調用domain層的PaymentRepository. 這時候會有一個麻煩的問題,通常來說Repository裏面都會依賴具體的ORM工具,比如hibernate或者Mybatis。但是前面說過我們希望Domain層是不要有任何外部依賴的,所以解決這個問題的方法就是在只在domain層定義PaymentRepository的接口,然後在Adapter層使用ORM工具實現這個接口。

ORM工具是在面向對象程序中進行數據庫持久化的好用工具,而且現在流行的ORM工具都提供了從數據庫表到實體類的自動生成功能,讓開發者儘可能的減少機械化的工作。但是這些好用的工具帶來的一個問題就是實體越來越貧血,實體裏面只是有一堆屬性和對應的get/set方法而已。但是在六邊形架構裏面,由於domain層的實體不需要由ORM生成,數據庫持久化相關的工作都在外面的adapter層中完成,所以開發者就可以放心的在domain中使用枚舉等各種編程語言的特性,還可以在實體中放心的實現各種本來就屬於實體的行爲。

思辨小結

這幾年由於微服務的原因六邊形架構也變得熱了起來。但是要真的用好六邊形架構,我們需要對這個概念有一個更深入的理解:知道它是怎麼提出來的?它解決的是什麼問題?爲什麼是六條邊?弄清楚了這些,在應用六邊形架構的時候才能清楚地知道什麼邏輯只能屬於哪一層,哪一層不能依賴哪一層。堅持這些原則的價值是:當有新需求或者需求發生變更時,重用代碼和修改代碼都是讓人愉悅的事情。

把使用ORM的數據庫持久化實現放在adapter的一個可能問題是,由於domain層的實體更貼近業務,所以這些實體模型無法直接給ORM使用,這就需要在adapter裏面再實現一套專門對應數據庫表的對象模型出來,並在adapter裏面吧domain的實體轉換成這些模型再進行數據庫操作。這樣看起來好像會有兩套冗餘的業務模型,一個在domain中,一個在adapter裏面。但其實只有domain中的纔是真正的業務模型,adapter裏面的模型只是數據庫表的一個映射對象而已。這種方式很好地把領域模型和持久化相關實現進行了解耦,我們可以完全拋開數據庫或者ORM的技術限制,去設計能夠真正和業務對應的領域模型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章