引言
前2篇序列文章的我們介紹了DDD戰略設計的一些理論和實踐,使用事件風暴完成了領域劃分和領域建模,劃分出的多個子域、領域模型、限界上下文、實體物件、值物件、聚合、聚合根等,這些結果將作為我們中臺和微服務設計的輸入。下面我們將進入戰術設計的階段,戰術設計的目標是完成程式碼的落地和實現。
DDD、中臺和微服務的關係DDD 與中臺和微服務的關係是:“中臺本質是領域中的某一個子域,需要抽象並標準化,按照單一職責原則建立可複用的領域模型。而微服務則是中臺的最佳技術實現。DDD 是一種可以同時指導中臺業務建模和微服務設計的方法論,它遵循高內聚低耦合的原則,完成從業務端領域拆分、建模到應用端微服務實現的無縫落地。”
DDD、中臺和微服務之間的關係
中臺的本質就是提煉各個業務板塊的共同需求,進行業務和系統抽象,形成通用的,可複用的業務模型,打造成元件化產品,供前臺部門使用。前臺要做什麼業務需要什麼資源,就可以直接找中臺,而不需要每次都去改動自己的底層。
DDD、中臺和微服務這三者出現的時代和背景不同,同樣的內容,在它們各自的理論體系中名詞術語和表達方式也存在很大的差異,各自論述的內容在企業內也分別屬於不同的維度。那它們三者之間是什麼樣的關係呢?
下圖就是分別從DDD領域建模的視角和中臺建設的視角對企業同一個領域業務架構的分解過程進行分析和對照。
DDD視角和中臺視角的領域分解過程
如果將企業內整個業務領域看作一個問題域的話,企業內的所有業務就是一個領域。在進行領域細分時,從DDD視角來看,子域可分為核心子域、通用子域和支撐子域,對應到中臺就是核心中臺和通用中臺。
從領域功能屬性範圍來看,子域與中臺同屬於業務子域,它們的功能邊界是一致的。領域模型所在的限界上下文對應微服務,限界上下文可以作為微服務拆分的依據。
微服務拆分原則理論上你可以將一個聚合拆分為一個微服務,以滿足軟體版本高頻釋出和極致彈性伸縮的要求,但建議不要對微服務過度拆分,這樣分增加運維和整合成本。根據實現情況可以把多個聚合放在一個微服務裡,例如把訂單和售後放在一個微服務裡也未嘗不可。
那微服務怎麼拆分?服務的粒度怎麼把控?有哪些拆分方法?可以參考李運華在極客時間的專欄《從0開始學架構》裡介紹的方法:
服務粒度
針對微服務拆分過細導致的問題,我建議基於團隊規模進行拆分,類似貝索斯在定義團隊規模時提出的“兩個披薩”理論(每個團隊的人數不能多到兩張披薩都不夠吃的地步),分享一個我認為微服務拆分粒度的“三個火槍手”原則,即一個微服務三個人負責開發。當我們在實施微服務架構時,根據團隊規模來劃分微服務數量,如果業務規繼續發展,團隊規模擴大,我們再將已有的微服務進行拆分。例如,團隊最初有 6 個人,那麼可以劃分為 2 個微服務,隨著業務的發展,業務功能越來越多,邏輯越來越複雜,團隊擴充套件到 12 個人,那麼我們可以將已有的 2 個微服務進行拆分,變成 4 個微服務。
為什麼是 3 個人,不是 4 個,也不是 2 個呢?
首先,從系統規模來講,3 個人負責開發一個系統,系統的複雜度剛好達到每個人都能全面理解整個系統,又能夠進行分工的粒度;如果是 2 個人開發一個系統,系統的複雜度不夠,開發人員可能覺得無法體現自己的技術實力;如果是 4 個甚至更多人開發一個系統,系統複雜度又會無法讓開發人員對系統的細節都瞭解很深。
其次,從團隊管理來說,3 個人可以形成一個穩定的備份,即使 1 個人休假或者調配到其他系統,剩餘 2 個人還可以支撐;如果是 2 個人,抽調 1 個後剩餘的 1 個人壓力很大;如果是 1 個人,這就是單點了,團隊沒有備份,某些情況下是很危險的,假如這個人休假了,系統出問題了怎麼辦?
最後,從技術提升的角度來講,3 個人的技術小組既能夠形成有效的討論,又能夠快速達成一致意見;如果是 2 個人,可能會出現互相堅持自己的意見,或者 2 個人經驗都不足導致設計缺陷;如果是 1 個人,由於沒有人跟他進行技術討論,很可能陷入思維盲區導致重大問題;如果是 4 個人或者更多,可能有的參與的人員並沒有認真參與,只是完成任務而已。
“三個火槍手”的原則主要應用於微服務設計和開發階段,如果微服務經過一段時間發展後已經比較穩定,處於維護期了,無須太多的開發,那麼平均 1 個人維護 1 個微服務甚至幾個微服務都可以。當然考慮到人員備份問題,每個微服務最好都安排 2 個人維護,每個人都可以維護多個微服務。
拆分方法
拆分方法基於“三個火槍手”的理論,我們可以計算出拆分後合適的服務數量,但具體怎麼拆也是有技巧的,並不是快刀砍亂麻隨便拆分成指定數量的微服務就可以了,也不是隻能按照業務來進行拆分,而是可以根據目的的不同靈活地選取不同的拆分方式。
接下來我一一介紹常見的拆分方式。
1. 基於業務邏輯拆分
這是最常見的一種拆分方式,將系統中的業務模組按照職責範圍識別出來,每個單獨的業務模組拆分為一個獨立的服務。基於業務邏輯拆分雖然看起來很直觀,但在實踐過程中最常見的一個問題就是團隊成員對於“職責範圍”的理解差異很大,經常會出現爭論,難以達成一致意見。例如:假設我們做一個電商系統,第一種方式是將服務劃分為“商品”“交易”“使用者”3 個服務,第二種方式是劃分為“商品”“訂單”“支付”“發貨”“買家”“賣家”6 個服務,哪種方式更合理,是不是劃分越細越正確?導致這種困惑的主要根因在於從業務的角度來拆分的話,規模粗和規模細都沒有問題,因為拆分基礎都是業務邏輯,要判斷拆分粒度,不能從業務邏輯角度,而要根據前面介紹的“三個火槍手”的原則,計算一下大概的服務數量範圍,然後再確定合適的“職責範圍”,否則就可能出現劃分過粗或者過細的情況,而且大部分情況下會出現過細的情況。例如:如果團隊規模是 10 個人支撐業務,按照“三個火槍手”規則計算,大約需要劃分為 4 個服務,那麼“登入、註冊、使用者資訊管理”都可以劃到“使用者服務”職責範圍內;如果團隊規模是 100 人支撐業務,服務數量可以達到 40 個,那麼“使用者登入“就是一個服務了;如果團隊規模達到 1000 人支撐業務,那“使用者連線管理”可能就是一個獨立的服務了。
2. 基於可擴充套件拆分
將系統中的業務模組按照穩定性排序,將已經成熟和改動不大的服務拆分為穩定服務,將經常變化和迭代的服務拆分為變動服務。穩定的服務粒度可以粗一些,即使邏輯上沒有強關聯的服務,也可以放在同一個子系統中,例如將“日誌服務”和“升級服務”放在同一個子系統中;不穩定的服務粒度可以細一些,但也不要太細,始終記住要控制服務的總數量。這樣拆分主要是為了提升專案快速迭代的效率,避免在開發的時候,不小心影響了已有的成熟功能導致線上問題。
3. 基於可靠性拆分
將系統中的業務模組按照優先順序排序,將可靠性要求高的核心服務和可靠性要求低的非核心服務拆分開來,然後重點保證核心服務的高可用。具體拆分的時候,核心服務可以是一個也可以是多個,只要最終的服務數量滿足“三個火槍手”的原則就可以。
這樣拆分帶來下面幾個好處:
1).避免非核心服務故障影響核心服務例如,日誌上報一般都屬於非核心服務,但是在某些場景下可能有大量的日誌上報,如果系統沒有拆分,那麼日誌上報可能導致核心服務故障;拆分後即使日誌上報有問題,也不會影響核心服務。2).核心服務高可用方案可以更簡單核心服務的功能邏輯更加簡單,儲存的資料可能更少,用到的元件也會更少,設計高可用方案大部分情況下要比不拆分簡單很多。3).能夠降低高可用成本將核心服務拆分出來後,核心服務佔用的機器、頻寬等資源比不拆分要少很多。因此,只針對核心服務做高可用方案,機器、頻寬等成本比不拆分要節省較多。4. 基於效能拆分
基於效能拆分和基於可靠性拆分類似,將效能要求高或者效能壓力大的模組拆分出來,避免效能壓力大的服務影響其他服務。常見的拆分方式和具體的效能瓶頸有關,可以拆分 Web 服務、資料庫、快取等。例如電商的搶購,效能壓力最大的是入口的排隊功能,可以將排隊功能獨立為一個服務。
以上幾種拆分方式不是多選一,而是可以根據實際情況自由排列組合,例如可以基於可靠性拆分出服務 A,基於效能拆分出服務 B,基於可擴充套件拆分出 C/D/F 三個服務,加上原有的服務 X,最後總共拆分出 6 個服務(A/B/C/D/F/X)。
微服務架構模型現在比較流行的微服務的架構模型有洋蔥架構、六邊形架構、DDD分層架構等,這些架構的核心理念是一樣的,都是為了設計出“高內聚、低耦合”的微服務,輕鬆實現高可擴充套件性的微服務應用。
下面我們會簡單介紹一下這三種架構的概念,分析比較它們的關係.
洋蔥架構
洋蔥架構,也稱為整潔架構(The Clean Architecture),用來構建具有如下特點的系統:
1. 獨立的Frameworks2. 可測試3. 獨立的UI4. 獨立的資料庫5. 獨立的任意外部服務(代理)
洋蔥架構原理圖
從它的原理圖可以理解它為什麼叫洋蔥架構了吧,它的核心思想是領域模型是最核心的,處在最內層,依賴關係是從外向裡,就是外層可以依賴內層,內層不能依賴外層。這樣很好理解,就是不管外部的環境如何變化,我的業務邏輯始終是不變的,就好比說不管你用語言,用什麼作業系統,用的是關係資料庫還是非關係資料庫,電商業務的邏輯始終是穩定的。下面這個圖更生動地來輔助我們理解洋蔥架構的原理。
Entities:應用的業務物件。
Use Casess: Use Casess協調(Orchestrate)資料從Entities的流入和流出,也被稱為Interactors。
Interface Adapters:這個Adapter集為Use Casess和Entities把資料轉換為方便使用的格式(如渲染展示在頁面上),Presenters和Controllers屬於這裡。
Frameworks and Drivers:這是實現所有細節的地方:UI,Tools,Frameworks等。
需要注意的是它這裡雖然畫了四個圈,但實際情況有可能不止四個圈,有可能是多個圈。
有興趣的朋友可以精讀這篇文章:https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
六邊形架構
六邊形架構的核心理念是:應用透過"埠"跟外部進行互動的。"埠"讓人聯想到作業系統的埠,任何符合協議的裝置都可以被插到相應的埠上。埠的協議是為了兩個裝置之間能夠進行通訊而設計的,位於OSI 7層協議模型中的傳輸層。
對應用來說,API就是協議。對於每一個外部裝置,都有對應的介面卡把API轉換成自己所需要的訊號,反之亦然。使用者圖形介面就是一個很好的例子,它是把使用者操作對映到埠API的介面卡,還有其它的例子如自動測試套件,批處理驅動器,以及任何需要跨應用程式互動的程式碼。
對於應用的資料處理層面,應用透過與外部實體互動得到資料,這裡用到的協議一般指資料庫協議。從應用角度來看,把SQL資料庫遷移到普通的檔案或者其它型別的資料庫,API仍然保持不變。適應於同一個埠的介面卡還包括SQL介面卡,檔案介面卡,以及更重要的資料庫mock,它可以是駐存在記憶體裡的資料庫,不一定是真實的資料庫。
很多應用都只有兩個埠:使用者端埠 和 資料庫端埠。這種情況看起來是對稱的,很自然地就會用單維度多層次的架構來構建它。在開發應用程式時,就是我們經常看到的三層、四層或五層架構。這種架構有兩個問題:
1.很容易跨越層間的邊界,把業務邏輯滲透到其它層中去。2.有的應用可能不止需要兩個埠,所以不能用單維度架構來構建。這就是六邊形架構提出的原因,它著重解決對稱性問題,應用透過埠與外部進行互動,而外部的實體也可以用同樣的方式來處理。六邊形架構強調以下兩點:
首先,透過"內外"的不對稱性以及埠的特點,擺脫單維度多層次架構的束縛。可以定義不同數量的埠,2個,3個或者4個,這裡說的六邊形不限於只有六個邊, 可以根據需要加入更多的埠和介面卡,"六邊形架構"只是視覺上的一種叫法。其次,關注整體架構的結果導向,一個埠對應一個或一組有目的互動行為。但一個埠一般會有多個介面卡,可以是無人應答機,語音留言機,按鍵電話,使用者圖形介面,測試套件,批處理驅動器,HTTP介面,程式之間的介面,mock的資料庫,或者真實的資料庫。從應用層面來看,這一架構的目的是將注意力聚焦在內外非對稱性上,讓外部的實體從應用視角來看都是一樣的。
六邊形架構
原文地址:https://alistair.cockburn.us/hexagonal-architecture/
DDD分層架構
DDD 分層架構共包含四層,分別是使用者介面層、應用層、領域層和基礎設施層。
DDD分層架構原理圖
領域層在分層架構最核心的位置,主要實現領域模型的最核心領域邏輯。領域層包括若干個聚合,每個聚合內有一個聚合根、若干實體和值物件。在微服務設計和開發時,要重點關注聚合之間物件和服務的解耦設計。一般來說聚合之間只通過聚合根 ID 關聯,應儘量避免不同聚合領域物件或服務以及資料實體之間產生依賴,以便微服務在架構演進時可以相對輕鬆地完成聚合的拆分和重組。
為了避免聚合之間產生服務依賴,聚合之間的資料互動一般採用非同步化的領域事件驅動方式。另外,你也可以將聚合之間的服務呼叫上升到應用層,在應用服務中完成不同聚合之間的服務組合和呼叫。
領域層之上是應用層,它連線使用者介面層和領域層,透過應用服務協調領域層多個聚合完成服務的組合和編排,形成粗粒度的組合服務。由於這一層基本不實現領域邏輯,所以它很薄。
再往上一層就是使用者介面層。微服務架構通常採用前後端分離設計,一個微服務可能會面對多個不同型別的前端應用。而前端應用由於技術或業務場景差異,對 API 的技術實現或資料可能會存在差異。在使用者介面層我們可以根據前端需求封裝應用層的應用服務,面向不同前端應用提供介面和資料適配。
基礎設施層為其他各層提供通用技術和基礎服務。透過依賴倒置設計,封裝基礎資源服務實現邏輯,實現各層與基礎設施層的解耦,降低外部資源和技術元件升級變化對核心業務邏輯的影響。
分層架構的好處是顯而易見的。
首先,由於層間鬆散的耦合關係,使得我們可以專注於本層的設計,而不必關心其他層的設計,也不必擔心自己的設計會影響其它層,對提高軟體質量大有裨益。其次,分層架構使得程式結構清晰,升級和維護都變得十分容易,更改某層的具體實現程式碼,只要本層的介面保持穩定,其他層可以不必修改。即使本層的介面發生變化,也隻影響相鄰的上層,修改工作量小且錯誤可以控制,不會帶來意外的風險。Martin Fowler在《Patterns of Enterprise Application Architecture》一書中總結了分層架構的一些優點:
開發人員可以只關注整個結構中的某一層。可以很容易地用新的實現來替換原有層次的實現。可以降低層與層之間的依賴。有利於標準化。利於各層邏輯的複用。DDD應用架構框架阿里的張健飛開源了一個DDD應用架構框架——Cola,github地址:https://github.com/alibaba/cola,他綜合了六邊形和洋蔥架構的思想,將業務複雜度和技術複雜度分離,各層之間透過防腐層進行通訊,盡最大可能的解耦,是一個不錯的學習DDD的框架。
小結本章主要介紹了服務設計的原則和拆分方法,和目前流行的微服務架構,分析它們的優缺點,最後分享了cola框架,想深入研的同學可以根據文提供的連結繼續學習。