如何基於 DDD 構建微服務架構
微服務構建本質上是軟件構建過程中長期演進積累的一系列理念、架構原則、工具和最佳實踐。
領域驅動設計的軟件思想體系和方法論可以用於指導微服務建模、微服務劃分、微服務架構設計等相關工作,它可以促使技術人員與領域專家達成共識,構建領域邊界合理、具備明確界限上下文、關注點分離、獨立自治的微服務。
01
領域驅動設計概述
領域驅動設計(Domain Driven Design)概念的興起可以追溯到 1986 年,《人月神話》的作者 Brooks 提出軟件的本質複雜性(Essential Complexity)存在於複雜的業務領域中,技術僅僅是輔助工具,它解決的問題是幫助業務領域從現實問題映射轉換成軟件實現。
領域驅動設計在戰略設計層面,從業務視角出發使技術人員專注於問題域,從領域專家那裏獲得領域見解,通過模塊劃分建立領域服務邊界,通過界限上下文明確服務的職責。
領域驅動設計在戰術設計層面,從技術的視角出發,提煉有效的業務模型,實施領域建模、架構設計完成軟件的落地。
領域驅動設計通過隔離業務與技術的複雜性,成爲程式化、標準化的軟件架構設計範式。
軟件複雜度的來源
-
業務的複雜性:業務的複雜性體現在業務流程不清晰、業務參與人員多、業務與技術耦合等方面。在業務的早期階段,爲了快速滿足功能需求容易形成麪條式的代碼風格,這樣的代碼風格會導致軟件模塊膨脹、開發效率降低、功能擴展步伐放緩、業務模型與代碼脫節等。
-
技術的複雜性:技術的複雜性來源於對項目的質量屬性需求,諸如系統的性能、客戶體驗、服務高可用性等。爲解決服務的響應延遲、吞吐、安全等問題,我們會引入緩存、消息隊列、第三方模塊組件,而這些技術的整合給系統引入了額外的複雜性和技術挑戰。
質量屬性需求:系統非功能(也叫非行爲)部分的需求。
領域驅動解決之道
解決這種軟件構建中面臨的複雜性問題,我們需要從領域開始着手,與業務專家一起獲得領域見解,促使軟件利益干係方在領域內建立通用語言。技術人員通過建模的手段提煉出事物的本質,以便更好地指導應用系統的構建和規劃。
領域驅動設計中包含了大量成熟的理論、概念、模式和架構,它包含一套解決複雜領域模型的軟件架構方法,思想是圍繞業務模型來連接和實現核心業務概念。
領域驅動設計可以讓業務和技術的變化產生的不可預知因素互相分離,將人員變動、團隊規模、協作溝通等外界因素變化對產品和項目的影響封裝在一個可控的容器和框架下,從而解決軟件面臨的複雜性問題,如下圖所示。
事務腳本模式與領域建模模式
-
事務腳本模式:事物腳本模式常見於單體應用中,它將所有邏輯全部組織在一個單一過程方法中,從數據庫的調用到不同業務邏輯、策略的執行全部集成在一個大的方法塊中。它的好處是簡單、容易實現,它的缺點是沒有自己的狀態,也無法擴展,容易將服務組件與數據存儲模型之間的剛性依賴引入業務邏輯中。
-
領域建模模式:領域建模模式將業務邏輯轉移到了領域對象(Domain Object)中,每個領域對象完成屬於自己的業務行爲。同時數據存儲層的邏輯也變得相對簡單,數據庫不再參與領域模型的業務邏輯,而是迴歸數據 “持久化” 的本質。
使用領域模式可以提升系統的內聚性和可重用性,通過不同類之間的協同完成所有功能。另外,多態的模式也讓擴展新的策略更加方便,業務語義更加通用、顯性化。領域建模過程遵循 “SOLID” 原則並實現業務域的邏輯解決方案。
說明:SOLID 原則
Single Responsibility Principle:單一職責原則
Open Closed Principle:開閉原則
Liskov Substitution Principle:里氏替換原則
Interface Segregation Principle:接口隔離原則
Dependence Inversion Principle:依賴倒置原則
領域驅動設計核心要素
如下圖所示是領域驅動設計的核心要素,包含領域驅動設計中的通用模型術語和重要的戰術模式。這些模式不僅可以捕獲和傳遞領域中的概念、關係及邏輯,也能幫助我們管理業務的複雜性並確保領域模型的行爲清晰明確。
-
領域:相對於軟件系統來說就是系統要解決的現實問題。
-
子域:對於領域進行不同維度切分的相對內聚的子系統單元。
-
分層架構:通過分層架構將業務域和技術邏輯域隔離。
-
服務:服務通常是領域對象的調用方,用來協調領域對象完成指定業務邏輯職責。
-
實體:實體與面向對象中的概念類似,它是領域模型的基本元素,在領域模型中,實體應該具有唯一的標識符。
-
值對象:值對象是沒有唯一標識符的實體。值對象在領域模型中是可以被共享的,它們應該是不可變的,當有其他地方需要用到值對象時,可以將它的副本作爲參數傳遞。
-
聚合:聚合使用邊界將內部和外部的對象劃分開來。每個聚合有一個根,這個根是一個實體作爲外部可以訪問的唯一對象。
-
資源庫:是封裝的所有獲取對象引用所需的邏輯單元。
-
工廠:工廠用來封裝對象創建所必需的信息,當聚合根建立時,所有聚合包含的對象將隨之建立。
02
專注問題域
解決一個業務場景中的複雜問題從理解問題域開始,通過專注於問題域並理解複雜問題背後的實質,你才能設計有效的模型來應對業務的挑戰。
在項目初期,儘量避免沉溺於技術實現,而要把焦點集中在問題領域,不要忘記技術服務業務的原則。
理解問題域
我們以一個金融場景下的 “業務運營監控系統” 爲例進行分析。經過與運營管理專家和相關業務方的多輪需求探論,我們初步瞭解了用戶的業務訴求和痛點。需要強調的是對於問題域的充分理解是我們的首要任務。
這裏整理了一份需求文檔,它詳細地記錄了問題域的具體範圍和詳細需求。這份文檔不僅是業務與技術團隊之間的一份溝通文檔,也可以作爲軟件生命週期在需求分析階段的一個清晰的、規範化的知識協作產物。
提煉問題域
理解複雜問題並從中識別、提煉出關鍵的業務模型,即提煉問題域是領域驅動設計的關鍵環節。團隊可以通過頭腦風暴的形式羅列出領域中的所有事件,整合之後形成最終的領域事件集合。
你需要在關鍵事件標記的範圍裏,參照不同利益干係方的業務訴求,組織領域事件和模型,同時,你需要整理出與項目關聯的上下游系統,如下圖所示。
通過挖掘隱藏在領域事件中的核心領域模型,我們可以找到從問題空間到方案空間的對應映射關係。針對上述業務監控系統案例,“進件存量”和 “進件流量” 的概念成爲我們發現的重要領域模型。
進件存量:是指在某一指定的時間點,過去生產與積累起來的進件的結存數量。
進件流量:單位時間內流過某一段管道的進件體積流量。
作爲衡量業務系統運轉狀態的重要指標,業務的 “存量” 狀態可以表示業務的積壓情況,而業務的 “流量” 狀態可以表示業務流轉的變化情況。
如下圖所示是我們總結的監控系統概要視圖,其中實線表示的是城市信貸業務工作流中進件在不同系統的流向,而虛線表示的則是業務的存量、流量在業務監控系統的事件記錄。
03
服務的拆分
完成問題域的理解和提煉後,我們需要對整體系統做進一步的服務拆分。下圖是我們根據業務領域能力對 “業務運營監控系統” 進行拆分後的子領域服務及模塊劃分說明。
- 業務事件收集(如下圖和表所示)
- 事件過濾聚合(如下圖和表所示)
- 規則配置(如下圖和表所示)
- 監控查詢展示(如下圖和表所示)
爲什麼要做服務拆分
-
降低系統的整體複雜性:根據業務領域進行合理的服務拆分是一個有效控制系統複雜性的方法。
-
提高效率:服務拆分後,代碼模塊相互隔離,併發的開發模式可以提升開發人員的效率。
-
團隊人員各司其職:拆分的項目可分派給擅長相關方面技術的人員,讓團隊成員各司其職,降低工作的耦合度。
-
共享和自治:可以通過定義好的服務接口進行服務共享,同時拆分後的服務也更加自治。
-
解決依賴問題:通過服務拆分,可以清晰地瞭解哪些服務依賴會對業務造成影響,從而準備預案。
服務拆分的依據
高內聚、低耦合是服務拆分的主要依據,下面我們列舉一些常用的服務拆分策略,瞭解如何對單體架構進行拆分。
-
區分服務類型:工具服務區別於業務服務,它的特點是與業務領域無關,根據其用途可以進一步細分,一般包括的形式有公共工具服務、資源工具服務、包裝器服務等。
-
根據功能定義劃分服務:領域驅動設計通過分析問題空間和業務邏輯,將應用程序定義爲域,域由多個子域組成,每個子域對應於業務的不同功能部分。
-
根據技術邊界劃分服務:對於產品類型的服務使用技術能力劃分服務邊界,前後端分離架構就是通過技術棧劃分服務邊界的典型架構模式。
服務拆分範式
通過增加服務實例或者機器來解決服務的容量和可用性問題是常用的可擴展架構解決方案。在《可擴展藝術》一書中提出了系統的可擴展性模型:AKF 可擴展立方,可以作爲服務拆分的範式。
AKF 可擴展立方:描述從單體應用到分佈式可擴展應用的可擴展模型。
如下圖所示是使用 Scale Cube 的 3D 模型實現的一個微服務架構模型,在 X 軸上通過 API 網關進行水平擴展,在 Y 軸上進行單體拆分後的微服務構建,服務之間可以通過 REST API 進行簡單交互,Z 軸是數據維度的拆分。
-
X 軸:服務擴展,通過克隆的方式水平擴展。一般是負載均衡後運行多個應用副本,達到某個服務的高吞吐量和高可用性。
-
Y 軸:功能拆分,通過拆分不同的事務進行擴展。微服務對應着 Y 軸,即將單體應用拆分爲微服務應用。
-
Z 軸:數據分區,通過分隔相同的事務進行擴展,例如數據庫分庫分表。
總之,服務支持水平擴展以提升容量;對功能的拆分體現在對業務模型的切入和深入理解上;應用數據的劃分是微服務的重要原則,如果數據的耦合問題無法解決,那麼應用服務的劃分還會有代碼耦合和級聯影響。
04
界限上下文
在找到服務邊界並把系統拆分後,我們需要使用 “界限上下文” 的概念明確服務之間的交互共享模型和行爲接口,它不僅可以有效地限定領域的職責邊界和特性範圍,也可以控制問題域的規模,進而以化整爲零的方式控制整個系統的複雜性。
在業務運營監控項目中,存量項模型作爲業務過濾聚合服務和存量查詢統計服務的共享模型,關係如下圖所示。
爲了實現捕獲和統計監控業務運營過程中的不同階段存量的業務狀態,我們將存量項作爲上述兩個服務上下文的共享模型,但我們不會暴露 “過濾聚合服務” 中的存量明細、Flow、Stream 等模塊的實現細節。
作爲兩個獨立的服務主體,它們應該在邊界上有明確的界線劃分和通信機制。如果服務邊界與領域的界限上下文能夠保持一致,那麼我們已經爲高內聚、低耦合的微服務架構實現了關鍵的一步。
05
領域建模
領域建模是領域驅動設計的核心,通過領域模型可以封裝對業務的抽象,建立業務概念與領域規則的關係。領域模型更關注的是業務語義的顯性表達,而不是具體的數據存儲及代碼邏輯實現細節,它可以有效地降低業務人員和技術人員之間的溝通成本。
案例分析
回到 “業務運營監控系統” 中,我們把業務監控的核心訴求聚焦在“業務事件”,以及業務的存量和流量領域模型。
在整理了領域服務的核心模塊後,我們可以把業務方關注的組織信息、業務類型信息、業務階段信息進行進一步領域模型細化,如下圖所示。
-
BizEvent:業務事件是業務監控的數據源,使用統一的 JSON 格式記錄消息事件,以日誌方式封裝當前業務系統發生的事件詳情。
-
Stream:對應一個端到端的數據流轉概念,通常我們會將 BizEvent 事件發送到 Kafka 的一個 Topic 上,通過建立 Stream 可以在消費端處理指定 Topic 上的數據流。
-
Flow:Flow 對應一個監控業務計算邏輯,存量 Flow 可以統計對應的存量狀態,流量 Flow 統計當前業務的流量狀態。
-
Service:它並非領域對象,表示一個通用的服務層,Service 提供業務存量和流量的查詢、備份、預警等業務方法。
-
Provision:用戶配置前置通用服務,不對應領域對象,主要接收用戶的配置請求,並保存爲業務規則。
-
Rule:即規則模型,屬於核心領域模型,業務方可以通過它靈活地定製關心的業務狀態並進行預警、過濾等。
-
Detail:屬於業務的中間監控過程詳情,屬於領域對象,同時包含組織、階段、業務類型等明細對象屬性(Org、Phase、BizType)。
使用領域建模的設計方法可以進一步將 “業務監控系統” 內部的領域服務與領域模型對象關聯,顯性地表達每個領域模型的具體工作職責及業務行爲事件與領域對象之間的上下文映射關係,如下圖所示。
06
架構設計
架構設計的本質是管理業務和技術複雜性,使系統易於有序化重構及擴展。高質量的架構一定是高度抽象的、圍繞業務的、易於理解的、面向演進的。
分層架構設計
領域驅動設計遵循 “關注點分離” 原則,將技術實現邏輯封裝在基礎設施層;將業務邏輯封裝在領域層,儘量使領域層代碼與其他層技術細節分割開來;將應用層作爲黏合劑,實現前兩者的協作;同時 UI 層可以基於 Swagger 技術暴露 REST API。分層架構如下圖所示。
六邊形(Hexagonal)架構模式
六邊形架構模式又稱爲 “端口 - 適配器” 模式,它將系統分爲內部和外部。內部代表應用的業務邏輯,外部代表應用的驅動邏輯、基礎設施或其他應用。內部以 API 接口呈現,通過端口和外部系統通信。外部系統需要使用不同的適配器,適配器負責對協議進行轉換。應用程序能夠以一致的方式與實際運行的設備和數據庫相隔離,方便開發和測試,六邊形架構模式如下圖所示。
微服務架構模式
微服務架構是強調細粒度、單一職責的架構模式。微服務架構更關注的是系統的非功能需求:質量屬性、演進能力、擴展性、觀測性、軟件交付效率等。微服務使用 CQRS(命令 / 查詢職責分離)中的事務腳本模式應對查詢場景,而對於複雜的業務邏輯場景,使用領域驅動設計模式。微服務架構模式如下圖所示。
本文內容摘自**《微服務架構深度解析:原理、實踐與進階》**一書,想了解更多微服務架構內容,歡迎閱讀此書!
**▊《**微服務架構深度解析:原理、實踐與進階》
王佩華 著
-
微服務架構領域集大成之作
-
國內鮮有的微服務詳解圖書
本書從微服務架構的設計理念和方法論切入,從不同角度全面介紹微服務特性、使用場景、組織流程、構建交互、部署交付等軟件工程各個關鍵環節和核心要素,既包含了具體微服務技術的源碼解讀、原理分析,也加入了作者在電信、金融領域積累的真實案例和實踐經驗。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/VPD9NSB1uAg5l4tx8DfXEQ