領(lǐng)域驅(qū)動設(shè)計(DDD)在微服務(wù)的實踐

“設(shè)計原則千萬條,高內(nèi)聚低耦合第一條,架構(gòu)設(shè)計不規(guī)范,開發(fā)運維兩行淚!”。

在分布式架構(gòu)下,單體應(yīng)用被拆分為多個微服務(wù),為了保證微服務(wù)的單一職責(zé)和合理拆分,“高內(nèi)聚、松耦合”是最寶貴的設(shè)計原則。

通俗點講,高內(nèi)聚就是把相關(guān)的行為聚集在一起,把不相關(guān)的行為放在別處,如果你要修改某個服務(wù)的行為,最好只在一處修改。如果做到了服務(wù)之間的松耦合,那么修改一個服務(wù)就不需要修改另一服務(wù),一個松耦合的服務(wù)應(yīng)該盡可能少的知道與之協(xié)作的那些服務(wù)的信息。

從集中式架構(gòu)向分布式架構(gòu)的技術(shù)轉(zhuǎn)型,正如從蓋磚瓦房向蓋高樓大廈轉(zhuǎn)變一樣,必然要有組織、文化、理念和設(shè)計方法的同步更新,其中最不可或缺的能力就是架構(gòu)設(shè)計能力。

如何做到“高內(nèi)聚、低耦合”?我們先來學(xué)習(xí)幾種典型的微服務(wù)架構(gòu)模型。

微服務(wù)架構(gòu)模型

整潔架構(gòu)(又名洋蔥架構(gòu))

在整潔架構(gòu)里,同心圓代表應(yīng)用軟件的不同部分,從里到外依次是領(lǐng)域模型、領(lǐng)域服務(wù)、應(yīng)用服務(wù)、最外圍是容易變化的內(nèi)容,如界面和基礎(chǔ)設(shè)施(如數(shù)據(jù)存儲等)。整潔架構(gòu)是以領(lǐng)域模型為中心,不是以數(shù)據(jù)為中心。

image

整潔架構(gòu)

整潔架構(gòu)最主要原則是依賴原則,它定義了各層的依賴關(guān)系,越往里,依賴越低,代碼級別越高。外圓代碼依賴只能指向內(nèi)圓,內(nèi)圓不知道外圓的任何事情。一般來說,外圓的聲明(包括方法、類、變量)不能被內(nèi)圓引用。同樣的,外圓使用的數(shù)據(jù)格式也不能被內(nèi)圓使用。

整潔架構(gòu)各層主要職能如下:

  • Entities:實現(xiàn)領(lǐng)域內(nèi)核心業(yè)務(wù)邏輯,它封裝了企業(yè)級的業(yè)務(wù)規(guī)則。一個 Entity 可以是一個帶方法的對象,也可以是一個數(shù)據(jù)結(jié)構(gòu)和方法集合。
  • Use Cases:實現(xiàn)與用戶操作相關(guān)的服務(wù)組合與編排,它包含了應(yīng)用特有的業(yè)務(wù)規(guī)則,封裝和實現(xiàn)了系統(tǒng)的所有用例。
  • Interface Adapters:它把適用于 Use Cases 和 entities 的數(shù)據(jù)轉(zhuǎn)換為適用于外部服務(wù)的格式,或把外部的數(shù)據(jù)格式轉(zhuǎn)換為適用于 Use Casess 和 entities 的格式。
  • Frameworks and Drivers:這是實現(xiàn)所有前端業(yè)務(wù)細節(jié)的地方:UI,Tools,F(xiàn)rameworks 等。

六邊形架構(gòu)(又名端口適配器架構(gòu))

追溯微服務(wù)架構(gòu)的淵源,一般會涉及到六邊形架構(gòu)。六邊形架構(gòu)的核心理念是:應(yīng)用是通過端口與外部進行交互的,這也是微服務(wù)架構(gòu)下 API 網(wǎng)關(guān)盛行的主要原因。六邊形架構(gòu)中,內(nèi)部業(yè)務(wù)邏輯(應(yīng)用層和領(lǐng)域模型)與外部資源(APP,WEB 應(yīng)用以及數(shù)據(jù)庫資源等)完全隔離,僅通過適配器進行交互。它解決了業(yè)務(wù)邏輯與用戶界面的代碼交錯的主要問題,從而可以很好的實現(xiàn)前后端分離。

image

六邊形架構(gòu)

六邊形架構(gòu)將系統(tǒng)分為內(nèi)部和外部兩層六邊形,內(nèi)部六邊形代表了應(yīng)用的核心業(yè)務(wù)邏輯,外部六邊形代表外部應(yīng)用、驅(qū)動和基礎(chǔ)資源等。內(nèi)部通過端口和適配器與外部通信,對應(yīng)用以 API 主動適配的方式提供服務(wù),對資源通過依賴反轉(zhuǎn)被動適配資源的形式呈現(xiàn)。一個端口可能對應(yīng)多個外部系統(tǒng),不同的外部系統(tǒng)使用不同的適配器,適配器負責(zé)對協(xié)議進行轉(zhuǎn)換。這就使得應(yīng)用程序能夠以一致的方式被用戶、程序、自動化測試、批處理腳本所驅(qū)動。

六邊形架構(gòu)各層的依賴關(guān)系與整潔架構(gòu)類似。

CQRS(命令與查詢職責(zé)分離)

CQRS 就是讀寫分離,讀寫分離的主要目的是為了提高查詢性能,同時達到讀、寫解耦。而 DDD 和 CQRS 結(jié)合,可以分別對讀和寫建模。

image

CQRS(命令與查詢職責(zé)分離)

查詢模型是一種非規(guī)范化數(shù)據(jù)模型,它不反映領(lǐng)域行為,只用于數(shù)據(jù)查詢和顯示;命令模型執(zhí)行領(lǐng)域行為,在領(lǐng)域行為執(zhí)行完成后通知查詢模型。

命令模型如何通知到查詢模型呢?如果查詢模型和領(lǐng)域模型共享數(shù)據(jù)源,則可以省卻這一步;如果沒有共享數(shù)據(jù)源,可以借助于發(fā)布訂閱的消息模式通知到查詢模型,從而達到數(shù)據(jù)最終一致性。

Martin 在 blog 中指出:CQRS 適用于極少數(shù)復(fù)雜的業(yè)務(wù)領(lǐng)域,如果不是很適合反而會增加復(fù)雜度;另一個適用場景是為了獲取高性能的查詢服務(wù)。

對于寫少讀多的共享類通用數(shù)據(jù)服務(wù)(如主數(shù)據(jù)類應(yīng)用)可以采用讀寫分離架構(gòu)模式。單數(shù)據(jù)中心寫入數(shù)據(jù),通過發(fā)布訂閱模式將數(shù)據(jù)副本分發(fā)到多數(shù)據(jù)中心。通過查詢模型微服務(wù),實現(xiàn)多數(shù)據(jù)中心數(shù)據(jù)共享和查詢。

領(lǐng)域驅(qū)動設(shè)計分層架構(gòu)

分層架構(gòu)的一個重要原則是每層只能與位于其下方的層發(fā)生依賴。

分層架構(gòu)的好處是顯而易見的。

首先,由于層間松散的耦合關(guān)系,使得我們可以專注于本層的設(shè)計,而不必關(guān)心其他層的設(shè)計,也不必擔(dān)心自己的設(shè)計會影響其它層,對提高軟件質(zhì)量大有裨益。其次,分層架構(gòu)使得程序結(jié)構(gòu)清晰,升級和維護都變得十分容易,更改某層的代碼,只要本層的接口保持穩(wěn)定,其他層可以不必修改。即使本層的接口發(fā)生變化,也只影響相鄰的上層,修改工作量小且錯誤可以控制,不會帶來意外的風(fēng)險。

關(guān)于分層架構(gòu)的權(quán)威觀點,Martin Fowler 在《Patterns of Enterprise Application Architecture》一書中給出了答案: 1. 開發(fā)人員只關(guān)注整個架構(gòu)中的某一層。 2. 很容易的用新的方法來替換原有層次的方法。 3. 降低層與層之間的依賴。 4. 有利于標準化。 5. 利于各層邏輯的復(fù)用。

要保持程序分層架構(gòu)的優(yōu)點,就必須堅持層間的松耦合關(guān)系。設(shè)計程序時,應(yīng)先劃分出可能的層次,以及此層次提供的接口和需要的接口。設(shè)計某層時,應(yīng)盡量保持層間的隔離,僅使用下層提供的接口。

image

DDD(領(lǐng)域驅(qū)動設(shè)計)分層架構(gòu)

DDD 分層架構(gòu)各層定義與職能:

展現(xiàn)層:它負責(zé)向用戶顯示信息和解釋用戶命令,完成前端界面邏輯。這里的用戶不一定是使用用戶界面的人,也可以是另一個計算機系統(tǒng)。

應(yīng)用層:它是很薄的一層,負責(zé)展現(xiàn)層與領(lǐng)域?qū)又g的協(xié)調(diào),也是與其它系統(tǒng)應(yīng)用層進行交互的必要渠道。應(yīng)用層要盡量簡單,不包含業(yè)務(wù)規(guī)則或者知識,不保留業(yè)務(wù)對象的狀態(tài),只保留有應(yīng)用任務(wù)的進度狀態(tài),更注重流程性的東西。它只為領(lǐng)域?qū)又械念I(lǐng)域?qū)ο髤f(xié)調(diào)任務(wù),分配工作,使它們互相協(xié)作。

領(lǐng)域?qū)樱核菢I(yè)務(wù)軟件的核心所在,包含了業(yè)務(wù)所涉及的領(lǐng)域?qū)ο螅▽嶓w、值對象)、領(lǐng)域服務(wù)以及它們之間的關(guān)系,負責(zé)表達業(yè)務(wù)概念、業(yè)務(wù)狀態(tài)信息以及業(yè)務(wù)規(guī)則,具體表現(xiàn)形式就是領(lǐng)域模型。領(lǐng)域驅(qū)動設(shè)計提倡富領(lǐng)域模型,即盡量將業(yè)務(wù)邏輯歸屬到領(lǐng)域?qū)ο笊希瑢嵲跓o法歸屬的部分則以領(lǐng)域服務(wù)的形式進行定義。

基礎(chǔ)設(shè)施層:它向其他層提供通用的技術(shù)能力,為應(yīng)用層傳遞消息(API 網(wǎng)關(guān)等),為領(lǐng)域?qū)犹峁┏志没瘷C制(如數(shù)據(jù)庫資源)等。

架構(gòu)模型對比和分析

雖然整潔架構(gòu)、六邊形架構(gòu)以及 DDD 分層架構(gòu)三種架構(gòu)模型展現(xiàn)方式以及解決問題的出發(fā)點不一樣,但其架構(gòu)思想與微服務(wù)架構(gòu)高內(nèi)聚低耦合的設(shè)計原則高度一致。

image

整潔、六邊形以及 DDD 三種架構(gòu)模型關(guān)系

突破現(xiàn)象看本質(zhì),在變與不變中尋找平衡!

從上圖可以看出,在六邊形架構(gòu)、DDD 分層架構(gòu)的白框部分以及整潔架構(gòu) Use Cases 和 Entities 區(qū)域?qū)崿F(xiàn)了核心業(yè)務(wù)邏輯。但是核心業(yè)務(wù)邏輯又由兩部分來完成:應(yīng)用層和領(lǐng)域?qū)舆壿嫛nI(lǐng)域?qū)訉崿F(xiàn)了最核心的業(yè)務(wù)領(lǐng)域部分的邏輯,對外提供領(lǐng)域模型內(nèi)細粒度的領(lǐng)域服務(wù),應(yīng)用層依賴領(lǐng)域?qū)訕I(yè)務(wù)邏輯,通過服務(wù)組合和編排通過 API 網(wǎng)關(guān)向前臺應(yīng)用提供粗粒度的服務(wù)。

系統(tǒng)需求變幻無窮,但變化總是有矩可循的,用戶體驗、操作習(xí)慣、市場環(huán)境以及管理流程的變化,往往會導(dǎo)致界面邏輯和流程的多變,但總體來說,不管前臺如何變化,核心領(lǐng)域邏輯基本不會大變。把握好這個規(guī)律,我們就知道如何設(shè)計應(yīng)用層和領(lǐng)域?qū)樱绾芜M行邏輯劃界了。

上述三種架構(gòu)模型正是通過分層方式來控制需求變化對系統(tǒng)的影響,確保從外向里受需求影響逐步減小。面向用戶的展現(xiàn)層可以快速響應(yīng)外部需求進行調(diào)整和發(fā)布,靈活多變,應(yīng)用層通過服務(wù)組合和編排實現(xiàn)業(yè)務(wù)流程的快速適配上線,領(lǐng)域?qū)踊揪筒恍枰嗟淖兓恕_@樣設(shè)計的好處是可以保證領(lǐng)域?qū)拥暮诵臉I(yè)務(wù)邏輯不會因為外部需求和流程的變動而調(diào)整,對于建立前臺靈活、中臺穩(wěn)固的架構(gòu)能力是很有好處的。

從幾種架構(gòu)模型看如何進行中臺及微服務(wù)設(shè)計?

中臺和微服務(wù)設(shè)計的關(guān)鍵在于合理的分層和領(lǐng)域模型的設(shè)計!

1、聚焦領(lǐng)域模型

中臺屬于后端業(yè)務(wù)領(lǐng)域邏輯范疇,重點關(guān)注領(lǐng)域內(nèi)業(yè)務(wù)邏輯的實現(xiàn),通過實現(xiàn)公共需求為前臺應(yīng)用提供共享服務(wù)能力。按 DDD 的方法,在領(lǐng)域模型建立的過程中會對業(yè)務(wù)和應(yīng)用進行清晰的邏輯和物理邊界劃分。領(lǐng)域模型的設(shè)計結(jié)果會影響到后續(xù)的系統(tǒng)模型、架構(gòu)模型和領(lǐng)域?qū)哟a模型的設(shè)計,最終影響到微服務(wù)的拆分和項目落地實施。

2、合理的架構(gòu)分層

不要把與領(lǐng)域無關(guān)的業(yè)務(wù)邏輯放在領(lǐng)域?qū)樱苊忸I(lǐng)域業(yè)務(wù)邏輯被污染,保證領(lǐng)域?qū)拥募儩崳挥羞@樣才能降低領(lǐng)域邏輯受外部變化的影響。在領(lǐng)域和架構(gòu)模型建立后,代碼模型的邏輯分層和微服務(wù)拆分要具體情況具體分析,根據(jù)自身研發(fā)和運維能力綜合考慮。

(1) 項目級單應(yīng)用

對于單應(yīng)用系統(tǒng)的分層,遵循上述分層架構(gòu)模型即可,核心領(lǐng)域邏輯在領(lǐng)域?qū)訉崿F(xiàn),服務(wù)的組合和編排在應(yīng)用層實現(xiàn),兩者組合形成中臺,通過 API 對前臺應(yīng)用提供服務(wù)。

從部署和微服務(wù)拆分來講,領(lǐng)域?qū)哟a部署時可能是一個微服務(wù),也可能會根據(jù)限界上下文被拆分為多個微服務(wù)部署。應(yīng)用層代碼如果邏輯復(fù)雜,含較多個性業(yè)務(wù)邏輯,可以根據(jù)需要獨立為微服務(wù)部署。如果邏輯簡單,且領(lǐng)域?qū)邮且粋€微服務(wù),在劃分好應(yīng)用層和領(lǐng)域?qū)哟a邏輯邊界的情況下,如果符合微服務(wù)拆分原則,也可以考慮將應(yīng)用層與領(lǐng)域?qū)哟a合并為一個微服務(wù)部署。

(2)企業(yè)級多中臺應(yīng)用

對于企業(yè)級多中臺應(yīng)用,多個中臺應(yīng)用通過 API 網(wǎng)關(guān)對外發(fā)布 API 服務(wù)。核心域業(yè)務(wù)中臺在調(diào)用支撐域和通用域中臺服務(wù)時通過核心域應(yīng)用層完成多中臺服務(wù)的組合和編排,為前臺應(yīng)用提供 API 服務(wù)。核心域中臺的應(yīng)用層是否獨立成微服務(wù)部署,需考慮的情況與單應(yīng)用系統(tǒng)相似。

3、服務(wù)的管理

應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)設(shè)施層都有對應(yīng)的服務(wù),各司其職提供服務(wù),其中基礎(chǔ)設(shè)施層的服務(wù)通過依賴反轉(zhuǎn)模式為領(lǐng)域?qū)雍蛻?yīng)用層提供基礎(chǔ)設(shè)施資源服務(wù)。應(yīng)用層和領(lǐng)域?qū)臃?wù)發(fā)布在 API 網(wǎng)關(guān),通過 API 網(wǎng)關(guān)適配,為前臺提供用戶無差異化(應(yīng)用 app、批處理或自動化測試)的服務(wù)。

4、資源的適配和解耦

由于上述架構(gòu)模型中定義的外層只能依賴內(nèi)層的架構(gòu)原則,對于像數(shù)據(jù)庫、緩存、文件系統(tǒng)等的外部基礎(chǔ)設(shè)施資源,往往采用依賴反轉(zhuǎn)的模式對外提供資源服務(wù),實現(xiàn)應(yīng)用層、領(lǐng)域?qū)优c基礎(chǔ)設(shè)施層資源的解耦。在設(shè)計中應(yīng)考慮資源層的代碼適配邏輯,一旦基礎(chǔ)設(shè)施資源出現(xiàn)變更(如換數(shù)據(jù)庫),可以屏蔽資源變更對業(yè)務(wù)代碼帶來的影響,切斷業(yè)務(wù)邏輯對基礎(chǔ)資源的依賴,降低由于資源變更對業(yè)務(wù)邏輯的影響。

5、前臺應(yīng)用

從核心業(yè)務(wù)邏輯來看,中臺實現(xiàn)了主要的業(yè)務(wù)邏輯,屬于標準化的重量級應(yīng)用。前臺應(yīng)用聚焦于界面交互以及業(yè)務(wù)流程等,屬于輕量級應(yīng)用,前臺應(yīng)用可以有個性的業(yè)務(wù)邏輯、流程和配置數(shù)據(jù),甚至數(shù)據(jù)庫,通過調(diào)用中臺 API 服務(wù)完成交互界面和業(yè)務(wù)全流程。

中臺、領(lǐng)域驅(qū)動設(shè)計及微服務(wù)

分析和設(shè)計模式的演進

在單機和集中式架構(gòu)時代,系統(tǒng)分析和設(shè)計往往都是分階段割裂進行的,容易導(dǎo)致需求、設(shè)計與代碼實現(xiàn)的不一致,軟件上線后才發(fā)現(xiàn)很多功能不是自己想要的,而且在這種模式下,軟件也不能快速響應(yīng)需求和業(yè)務(wù)變化。

領(lǐng)域驅(qū)動設(shè)計(DDD)打破了這種隔閡,它提出了領(lǐng)域模型概念,統(tǒng)一了分析、設(shè)計和開發(fā)語言和過程,使得軟件能夠更靈活快速響應(yīng)需求變化。

軟件分析和設(shè)計方法經(jīng)歷了三個階段的演進:

  • 第一階段是單機架構(gòu)時代:采用面向過程的設(shè)計方法,系統(tǒng)包括 UI 層和數(shù)據(jù)庫兩層,采用 C/S 架構(gòu)模式,整個系統(tǒng)圍繞數(shù)據(jù)庫驅(qū)動設(shè)計和開發(fā),新項目總是從設(shè)計數(shù)據(jù)庫及其字段開始。
  • 第二階段是集中式架構(gòu)時代:采用面向?qū)ο蟮脑O(shè)計方法,系統(tǒng)包括 UI 層、業(yè)務(wù)邏輯層和數(shù)據(jù)庫層,采用經(jīng)典的三層架構(gòu),也有部分應(yīng)用采用傳統(tǒng)的 SOA 架構(gòu),這種架構(gòu)易使服務(wù)變得臃腫,難于維護拓展,伸縮性能差。這個階段系統(tǒng)分析、軟件設(shè)計和開發(fā)大多是分階段進行的。
  • 第三階段是分布式架構(gòu)時代:由于微服務(wù)架構(gòu)的流行,采用領(lǐng)域驅(qū)動設(shè)計方法,應(yīng)用系統(tǒng)包括 UI 層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)層。這個階段融合了分析和設(shè)計階段,通過建立領(lǐng)域模型,劃分領(lǐng)域邊界,做到領(lǐng)域模型既設(shè)計,代碼與設(shè)計保持一致。

領(lǐng)域驅(qū)動設(shè)計主要優(yōu)勢:1. 業(yè)務(wù)導(dǎo)向。2. 業(yè)務(wù)邏輯內(nèi)聚,應(yīng)用邊界清晰。3. 建立領(lǐng)域模型優(yōu)先。4. 分析、設(shè)計、代碼和數(shù)據(jù)有機結(jié)合。5. 代碼即設(shè)計。6. 擴展性好。

數(shù)據(jù)驅(qū)動設(shè)計主要特點:1. 技術(shù)導(dǎo)向。2. 數(shù)據(jù)庫優(yōu)先。3. 代碼不能反映業(yè)務(wù)和設(shè)計。4. 業(yè)務(wù)邏輯分散。5. 擴展性不好。

領(lǐng)域驅(qū)動設(shè)計概述

2004 年 Eric Evans 發(fā)表《Domain-Driven Design –Tackling Complexity in the Heart of Software》 (領(lǐng)域驅(qū)動設(shè)計 )簡稱 Evans DDD。但在軟件開發(fā)領(lǐng)域一直都是雷聲大,雨點小,領(lǐng)域驅(qū)動設(shè)計核心思想是通過領(lǐng)域驅(qū)動設(shè)計方法定義領(lǐng)域模型,從而確定業(yè)務(wù)和應(yīng)用邊界,保證業(yè)務(wù)模型與代碼模型的一致性。這幾年之所以開始火起來,主要功勞要歸功于隊友“微服務(wù)”,領(lǐng)域驅(qū)動設(shè)計與微服務(wù)架構(gòu)天生匹配。

領(lǐng)域驅(qū)動設(shè)計(DDD)是一種處理高度復(fù)雜域的設(shè)計思想,試圖分離技術(shù)實現(xiàn)的復(fù)雜性,圍繞業(yè)務(wù)概念構(gòu)建領(lǐng)域模型來控制業(yè)務(wù)的復(fù)雜性,以解決軟件難以理解,難以演化等問題。團隊利用它可以成功的開發(fā)復(fù)雜業(yè)務(wù)軟件系統(tǒng),在系統(tǒng)變大時仍能保持敏捷性。

領(lǐng)域驅(qū)動設(shè)計分為兩個階段:

  1. 以一種領(lǐng)域?qū)<摇⒃O(shè)計人員、開發(fā)人員都能理解的通用語言作為相互交流的工具,在交流的過程中發(fā)現(xiàn)領(lǐng)域概念,然后將這些概念設(shè)計成一個領(lǐng)域模型;
  2. 由領(lǐng)域模型驅(qū)動軟件設(shè)計,用代碼來實現(xiàn)該領(lǐng)域模型。

領(lǐng)域驅(qū)動設(shè)計的核心訴求是讓業(yè)務(wù)架構(gòu)和系統(tǒng)架構(gòu)形成綁定關(guān)系,當(dāng)我們?nèi)ロ憫?yīng)業(yè)務(wù)變化調(diào)整業(yè)務(wù)架構(gòu)時,系統(tǒng)架構(gòu)的改變也會隨之發(fā)生。在領(lǐng)域驅(qū)動設(shè)計中業(yè)務(wù)架構(gòu)的梳理和系統(tǒng)架構(gòu)的梳理是同步進行的,其結(jié)果是設(shè)計出的業(yè)務(wù)上下文和系統(tǒng)模塊結(jié)構(gòu)是綁定的。同時技術(shù)架構(gòu)也是解耦的,可以根據(jù)劃分出來的業(yè)務(wù)上下文的系統(tǒng)架構(gòu)選擇最合適的實現(xiàn)技術(shù)。

領(lǐng)域驅(qū)動設(shè)計包括戰(zhàn)略設(shè)計和戰(zhàn)術(shù)設(shè)計兩個部分。戰(zhàn)略設(shè)計主要關(guān)注按領(lǐng)域定義,在限界上下文內(nèi)形成統(tǒng)一語言,提升業(yè)務(wù)和技術(shù)的溝通效率; 戰(zhàn)術(shù)設(shè)計主要關(guān)注領(lǐng)域設(shè)計在落地時與設(shè)計模型及實現(xiàn)模型的差異性,減小業(yè)務(wù)和技術(shù)之間的鴻溝。(本文對 DDD 知識點不做詳述,如需了解或?qū)W習(xí),請查閱《領(lǐng)域驅(qū)動設(shè)計:軟件核心復(fù)雜性應(yīng)對之道》和《實現(xiàn)領(lǐng)域驅(qū)動》)。

領(lǐng)域驅(qū)動設(shè)計可能會給你帶來以下收獲:

1、領(lǐng)域驅(qū)動設(shè)計是一套完整而系統(tǒng)的設(shè)計方法,它能帶給你從戰(zhàn)略設(shè)計到戰(zhàn)術(shù)設(shè)計的規(guī)范過程,使得你的設(shè)計思路能夠更加清晰,設(shè)計過程更加規(guī)范。

2、領(lǐng)域驅(qū)動設(shè)計尤其善于處理與領(lǐng)域相關(guān)的高復(fù)雜度業(yè)務(wù)的產(chǎn)品研發(fā),通過它可以為你的產(chǎn)品建立一個核心而穩(wěn)定的領(lǐng)域模型內(nèi)核,有利于領(lǐng)域知識的傳遞與傳承。

3、領(lǐng)域驅(qū)動設(shè)計強調(diào)團隊與領(lǐng)域?qū)<业暮献鳎軌驇椭鷪F隊建立一個溝通良好的團隊組織,構(gòu)建一致的架構(gòu)體系。 領(lǐng)域驅(qū)動設(shè)計強調(diào)對架構(gòu)與模型的精心打磨,尤其善于處理系統(tǒng)架構(gòu)的演進設(shè)計。

4、領(lǐng)域驅(qū)動設(shè)計的思想、原則與模式有助于提高團隊成員的架構(gòu)設(shè)計能力。

5、領(lǐng)域驅(qū)動設(shè)計與微服務(wù)架構(gòu)天生匹配,無論是在新項目中設(shè)計微服務(wù)架構(gòu),還是將系統(tǒng)從單體架構(gòu)演進到微服務(wù)設(shè)計,都可以遵循領(lǐng)域驅(qū)動設(shè)計的架構(gòu)原則。

為什么領(lǐng)域驅(qū)動設(shè)計是微服務(wù)架構(gòu)的最佳設(shè)計方法?

領(lǐng)域驅(qū)動設(shè)計作為一種架構(gòu)設(shè)計方法,微服務(wù)作為一種架構(gòu)風(fēng)格,兩者從本質(zhì)上都是為追求高響應(yīng)力目標而從業(yè)務(wù)視角去分離復(fù)雜度的手段。 兩者都強調(diào)從業(yè)務(wù)出發(fā),其核心要義強調(diào)根據(jù)業(yè)務(wù)發(fā)展,合理劃分領(lǐng)域邊界,持續(xù)調(diào)整現(xiàn)有架構(gòu),優(yōu)化現(xiàn)有代碼,以保持架構(gòu)和代碼的生命力(演進式架構(gòu)) 。

領(lǐng)域驅(qū)動設(shè)計主要關(guān)注:業(yè)務(wù)領(lǐng)域,劃分領(lǐng)域邊界;構(gòu)建通用語言,高效溝通;對業(yè)務(wù)進行抽象,建立領(lǐng)域模型;維持業(yè)務(wù)和代碼的邏輯一致性。

微服務(wù)主要關(guān)注:運行時進程間通信,能夠容錯和故障隔離;去中心化管理數(shù)據(jù)和去中心化治理;服務(wù)可以獨立的開發(fā)、測試、構(gòu)建和部署,按業(yè)務(wù)組織全功能團隊;高內(nèi)聚低耦合,職責(zé)單一。

如果你的業(yè)務(wù)焦點在領(lǐng)域和領(lǐng)域邏輯,那么你就可以選擇 DDD 進行微服務(wù)架構(gòu)設(shè)計。

中臺、DDD 與微服務(wù)

中臺的定義來源于阿里的中臺戰(zhàn)略(詳見《企業(yè) IT 架構(gòu)轉(zhuǎn)型之道:阿里巴巴中臺戰(zhàn)略思想與架構(gòu)實戰(zhàn)》鐘華編著)。2015 年年底,阿里巴巴集團對外宣布全面啟動阿里巴巴集團 2018 年中臺戰(zhàn)略,構(gòu)建符合數(shù)字時代的更具創(chuàng)新性、靈活性的“大中臺、小前臺”組織機制和業(yè)務(wù)機制,即作為前臺的一線業(yè)務(wù)會更敏捷、更快速適應(yīng)瞬息萬變的市場,而中臺將集合整個集團的運營數(shù)據(jù)能力、產(chǎn)品技術(shù)能力,對各前臺業(yè)務(wù)形成強力支撐。

中臺的本質(zhì)是提煉各個業(yè)務(wù)條線的共同需求,并將這些功能打造成組件化產(chǎn)品,然后以 API 接口的形式提供給前臺各業(yè)務(wù)部門使用。前臺要做什么業(yè)務(wù),需要什么資源可以直接找中臺,不需要每次去改動自己的底層,而是在底層不變動的情況下,在更豐富靈活的“大中臺”基礎(chǔ)上獲取支持,讓“小前臺”更加靈活敏捷。

中臺戰(zhàn)略的主要目標是實現(xiàn)公共需求和功能的中臺化共享,減少重復(fù)建設(shè)和投入,為前臺提供統(tǒng)一的一致服務(wù)。至于前臺應(yīng)用是否可以有數(shù)據(jù)庫?抑或采用什么樣的開發(fā)技術(shù),這些都不是重點,重點需要考慮的是那些公共需求和需要共享的功能是否通過中臺的方式被前臺使用了。

領(lǐng)域驅(qū)動設(shè)計中領(lǐng)域的定義:一個領(lǐng)域本質(zhì)上可以理解為就是一個問題域,只要是同一個領(lǐng)域,那問題域就相同。所以只要我們確定了系統(tǒng)所屬的領(lǐng)域,那這個系統(tǒng)的核心業(yè)務(wù),即要解決的關(guān)鍵問題、問題的范圍邊界就基本確定了。領(lǐng)域的本質(zhì)是問題域,問題域可能根據(jù)需要逐層細分,因此領(lǐng)域可分解為子域,子域或可繼續(xù)分為子子域。。。

在領(lǐng)域驅(qū)動設(shè)計中根據(jù)重要性與功能屬性將領(lǐng)域分為三類子域,分別是:核心子域、支撐子域和通用子域。決定產(chǎn)品和企業(yè)獨特競爭力的子域是核心子域,它是業(yè)務(wù)成功的主要因素和企業(yè)的核心競爭力。沒有個性化的訴求,屬于通用功能的子域是通用子域,如登陸認證。 還有一種所提供的功能是必須的,但不是通用也不是企業(yè)核心競爭力的子域是支撐子域,如單證。

image

DDD: 核心域、支撐域和通用域

中臺、領(lǐng)域以及微服務(wù)屬于不同層面的內(nèi)容,稍作分解我們理清他們之間的關(guān)系。

以保險領(lǐng)域為例,業(yè)務(wù)中臺大致可分為兩類:第一類是提供保險核心業(yè)務(wù)服務(wù)的專屬業(yè)務(wù)中臺(如承保、理賠等業(yè)務(wù));第二類是支撐核心業(yè)務(wù)流程完成保險全流程的通用中臺(如主數(shù)據(jù)、客戶、用戶以及電子保單等)。

專屬業(yè)務(wù)中臺是保險企業(yè)的核心競爭力,對應(yīng) DDD 的核心子域。通用中臺對應(yīng) DDD 支撐子域和通用子域。不同領(lǐng)域可根據(jù)領(lǐng)域大小進一步細分多個子域,多個子域可對應(yīng)到一個業(yè)務(wù)中臺,一個業(yè)務(wù)中臺也可能會分解成多個子域。

image

中臺、領(lǐng)域以及微服務(wù)

微服務(wù)是技術(shù)實現(xiàn)和部署的范疇,實現(xiàn)領(lǐng)域或中臺的業(yè)務(wù)邏輯,為前臺應(yīng)用提供服務(wù)。領(lǐng)域根據(jù)限界上下文可以設(shè)計為多個微服務(wù),而如果限界上下文過大,一個微服務(wù)也可能會包含多個子領(lǐng)域。

中臺是由多個業(yè)務(wù)條線的共同需求所構(gòu)成,是需要共享的業(yè)務(wù)功能和服務(wù)單元的集合,一個中臺可由一個微服務(wù)來實現(xiàn),也可根據(jù)領(lǐng)域驅(qū)動設(shè)計和微服務(wù)拆分原則細分為多個微服務(wù),多個微服務(wù)功能集合共同組成一個中臺。

基于 DDD 的微服務(wù)設(shè)計方法

DDD 設(shè)計包括戰(zhàn)略設(shè)計和戰(zhàn)術(shù)設(shè)計兩個部分。在戰(zhàn)略設(shè)計階段主要完成領(lǐng)域建模和服務(wù)地圖。在戰(zhàn)術(shù)設(shè)計階段,通過聚合、實體、值對象以及不同層級的服務(wù),完成微服務(wù)的建設(shè)和實施。通過 DDD 可以保證業(yè)務(wù)模型、系統(tǒng)模型、架構(gòu)模型以及代碼模型的一致。

本部分主要討論領(lǐng)域設(shè)計方法,如對戰(zhàn)術(shù)設(shè)計和開發(fā)方法感興趣可查閱 DDD 戰(zhàn)術(shù)設(shè)計相關(guān)資料。

DDD 領(lǐng)域設(shè)計過程包括產(chǎn)品愿景、場景分析、領(lǐng)域建模和服務(wù)地圖階段,也可根據(jù)需要裁剪不必要的階段和參與角色。領(lǐng)域驅(qū)動設(shè)計一般經(jīng)歷 2-6 周的時間,領(lǐng)域模型設(shè)計完成后,即可投入微服務(wù)實施。

1、產(chǎn)品愿景

產(chǎn)品愿景是對產(chǎn)品的頂層價值設(shè)計,對產(chǎn)品目標用戶、核心價值、差異化競爭點等策略層信息達成一致,避免產(chǎn)品在演進過程中偏離方向。

  • 階段輸入:產(chǎn)品初衷、用戶研究、競品知識和差異性想法 。
  • 參與角?:業(yè)務(wù)需求方、產(chǎn)品經(jīng)理、開發(fā)組長和產(chǎn)品發(fā)起人。
  • 階段產(chǎn)出:電梯演講畫布。

2、場景分析

場景分析是針對核心用戶及頂層服務(wù)的一種定性分析,從?戶視角出發(fā),探索問題域中的典型場景分析。同時也是從用戶視角對問題域的探索,產(chǎn)出問題域中需要支撐的場景分類及典型場景,用以支撐領(lǐng)域建模階段。

  • 階段輸?:核?干系人和服務(wù)價值定位。
  • 參與角色:產(chǎn)品經(jīng)理、開發(fā)組長和測試組長。
  • 階段產(chǎn)出:場景分類清單。

3、領(lǐng)域建模

領(lǐng)域建模是通過對業(yè)務(wù)和問題域進?分析,建?領(lǐng)域模型,向上通過限界上下?指導(dǎo)微服務(wù)的邊界設(shè)計,向下通過聚合指導(dǎo)實體的對象設(shè)計。領(lǐng)域建模主要采用事件風(fēng)暴方法。

  • 階段輸入:業(yè)務(wù)領(lǐng)域知識和場景分類清單。
  • 參與角色:領(lǐng)域?qū)<摇⒓軜?gòu)師、產(chǎn)品經(jīng)理、開發(fā)組長和測試組長。
  • 階段產(chǎn)出:聚合模型和限界上下?地圖。

4、服務(wù)地圖

服務(wù)地圖是整個產(chǎn)品服務(wù)架構(gòu)的體現(xiàn)。結(jié)合業(yè)務(wù)與技術(shù)因素,對服務(wù)的粒度、邊界劃分、集 成關(guān)系進?梳理,得到反映系統(tǒng)微服務(wù)層面設(shè)計的服務(wù)地圖。

  • 階段輸?:限界上下?地圖。
  • 參與角?:產(chǎn)品經(jīng)理、開發(fā)組長、測試組長和產(chǎn)品發(fā)起人。
  • 階段產(chǎn)出:服務(wù)地圖。

在進行服務(wù)地圖設(shè)計時需要考慮以下要素:1. 圍繞限界上下?邊界。2. 考慮不同業(yè)務(wù)變化速度 / 相關(guān)度、發(fā)布頻率。3. 考慮系統(tǒng)非功能性需求,如系統(tǒng)彈性伸縮要求、安全性要求和可?性要求。4. 考慮團隊組織和溝通效率。5. 軟件包限制。6. 技術(shù)和架構(gòu)的異構(gòu)。

通過 DDD 戰(zhàn)略和戰(zhàn)術(shù)全流程設(shè)計可建立業(yè)務(wù)架構(gòu)與系統(tǒng)架構(gòu)的一一映射,保證業(yè)務(wù)和代碼模型的一致性。

image

DDD 的業(yè)務(wù)架構(gòu)與系統(tǒng)架構(gòu)映射建立過程

DDD 分層架構(gòu)中的服務(wù)

前面我們談到了 DDD 的分層架構(gòu),分層架構(gòu)主要包括:展現(xiàn)層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)層(參考圖:DDD(領(lǐng)域驅(qū)動設(shè)計)分層架構(gòu)),各層都有不同的服務(wù),但由于各層職責(zé)不一樣,服務(wù)目的和實現(xiàn)方式也存在差異。

1、應(yīng)用層服務(wù)

應(yīng)用層是很瘦的一層,其服務(wù)主要用來表述應(yīng)用和用戶行為。它主要負責(zé)服務(wù)的組合、編排和轉(zhuǎn)發(fā),負責(zé)處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的拼裝,拼裝完領(lǐng)域服務(wù)后以粗粒度的服務(wù)通過 API 網(wǎng)關(guān)向前臺應(yīng)用發(fā)布。通過這樣一種方式,隱藏了領(lǐng)域?qū)拥膹?fù)雜性及其內(nèi)部實現(xiàn)機制。 應(yīng)用層除了定義應(yīng)用服務(wù)之外,在這層還可以進行安全認證,權(quán)限校驗,持久化事務(wù)控制或向其他系統(tǒng)發(fā)送基于事件的消息通知。

2、領(lǐng)域?qū)臃?wù)

領(lǐng)域?qū)邮禽^“胖”的一層,它實現(xiàn)了全部業(yè)務(wù)邏輯并且通過各種校驗手段保證業(yè)務(wù)正確性。業(yè)務(wù)邏輯包括:業(yè)務(wù)流程、業(yè)務(wù)策略、業(yè)務(wù)規(guī)則、完整性約束等。 當(dāng)領(lǐng)域中的某個操作過程或轉(zhuǎn)換過程不是實體或值對象的職責(zé)時,便將該操作放在一個單獨的服務(wù)接口中,這就是領(lǐng)域服務(wù),領(lǐng)域服務(wù)是無狀態(tài)的。

3、基礎(chǔ)設(shè)施層服務(wù)

基礎(chǔ)設(shè)施層服務(wù)位于基礎(chǔ)設(shè)施層,根據(jù)依賴倒置原則,封裝基礎(chǔ)資源服務(wù),實現(xiàn)資源層與應(yīng)用層和領(lǐng)域?qū)拥恼{(diào)用依賴反轉(zhuǎn),為應(yīng)用層和領(lǐng)域?qū)犹峁┗A(chǔ)資源服務(wù)(如數(shù)據(jù)庫、緩存等基礎(chǔ)資源),實現(xiàn)各層的解耦,降低外部資源的變化對核心業(yè)務(wù)邏輯的影響。

4、總結(jié)

應(yīng)用層服務(wù)是展現(xiàn)層和領(lǐng)域?qū)拥臉蛄海ㄟ^調(diào)用領(lǐng)域?qū)ο蠛皖I(lǐng)域?qū)臃?wù)來表達用例和用戶故事。領(lǐng)域?qū)ο筘撠?zé)單一操作, 領(lǐng)域?qū)臃?wù)用于協(xié)調(diào)多個領(lǐng)域?qū)ο蠊餐瓿赡硞€業(yè)務(wù)操作。 應(yīng)用服務(wù)原則上不處理業(yè)務(wù)邏輯,領(lǐng)域服務(wù)處理業(yè)務(wù)邏輯。

微服務(wù)的邊界設(shè)計

邏輯邊界與物理邊界

在領(lǐng)域模型設(shè)計時,我們通常會根據(jù)限界上下文將領(lǐng)域分解成不同的子域,劃分業(yè)務(wù)領(lǐng)域的邏輯邊界。在限界上下文內(nèi)不同的實體和值對象可以組合成不同的聚合,從而形成聚合與聚合之間的邏輯邊界。一般來說,限界上下文可以作為微服務(wù)拆分的依據(jù),而限界上下文內(nèi)的聚合由于其業(yè)務(wù)邏輯的高度內(nèi)聚,也可以根據(jù)需要將同一領(lǐng)域內(nèi)的聚合業(yè)務(wù)邏輯代碼拆分為微服務(wù),聚合是領(lǐng)域中可以拆分為微服務(wù)的最小單元。

限界上下文與限界上下文之間以及聚合與聚合之間的邊界是邏輯邊界,微服務(wù)與微服務(wù)的邊界是物理邊界。邏輯邊界強調(diào)業(yè)務(wù)領(lǐng)域邏輯或代碼分層的隔離,物理邊界強調(diào)部署和運行的隔離。

微服務(wù)設(shè)計時是否一定要做到邏輯邊界與物理邊界一致?

邏輯邊界的劃分是否可以細于物理邊界?

過度的微服務(wù)拆分會導(dǎo)致服務(wù)、安全和運維管理更復(fù)雜,領(lǐng)域之間的服務(wù)協(xié)同或應(yīng)用層的處理邏輯更復(fù)雜,總之一句話就是:需要更高的研發(fā)技能要求和軟件維護成本。因此領(lǐng)域和代碼分層的邏輯邊界的細分是必要的,但是物理邊界不宜過細,也就是說在不違反微服務(wù)拆分原則的情況下,不宜過度拆分微服務(wù)。

為什么要細分業(yè)務(wù)和代碼邏輯邊界?

在從單體向微服務(wù)演進后,隨著新需求的出現(xiàn),新的微服務(wù)會開始慢慢的膨脹起來,有一天你會發(fā)現(xiàn)膨脹的微服務(wù)有一部分業(yè)務(wù)能力需要拆分出去時,如果沒有提前進行邏輯邊界的細分,微服務(wù)內(nèi)代碼的過度耦合將會讓你無從下手,你是否還需要再做一次從單體向微服務(wù)的拆分?

如果你在微服務(wù)設(shè)計時已經(jīng)根據(jù)業(yè)務(wù)領(lǐng)域邊界提前進行了領(lǐng)域代碼的分層和邏輯隔離,在微服務(wù)再次拆分時,分別對邏輯分離的領(lǐng)域代碼打包,同步進行數(shù)據(jù)庫拆分,就可以快速完成微服務(wù)的拆分,而不需要重復(fù)從單體應(yīng)用向微服務(wù)痛苦的演進過程。

當(dāng)然,在同一個微服務(wù)內(nèi)邏輯隔離的代碼,在內(nèi)部領(lǐng)域服務(wù)之間調(diào)用以及數(shù)據(jù)訪問設(shè)計上需要有合理的松耦合的設(shè)計和開發(fā)規(guī)范,否則也不能很快的完成微服務(wù)再次拆分。

總之,我們需要內(nèi)外部邏輯邊界清晰的微服務(wù),而不是從一個大單體重構(gòu)為多個小單體。

要做微服務(wù)而不是小單體

很多時候大家對微服務(wù)設(shè)計的理解都以為只要最后確定拆分出多少個微服務(wù)就可以了,其實拆成多少個微服務(wù)并不是微服務(wù)架構(gòu)的要點。如何設(shè)計或拆分才能避免拆分出來的微服務(wù)不是小單體?這才是所有微服務(wù)架構(gòu)團隊需要關(guān)注和解決的問題,這也是 DDD 的價值所在。

image

要做微服務(wù)而不是小單體

評判微服務(wù)設(shè)計合理的一個簡單標準就是:微服務(wù)在隨著業(yè)務(wù)發(fā)展而不斷拆分或者重新組合過程中不會過度增加軟件維護成本,并且這個過程是非常輕松且簡單的。

微服務(wù)代碼邏輯分層和結(jié)構(gòu)

為了方便在微服務(wù)變大時實現(xiàn)快樂的拆分和合并,在明確各層代碼職責(zé)后,我們需要對微服務(wù)代碼合理分層和邏輯隔離,以下圖為例對代碼分層和結(jié)構(gòu)進行簡要說明。

基礎(chǔ)層代碼:本層主要包括兩類適配代碼:主動適配和被動適配。主動適配代碼主要面向前端應(yīng)用提供 API 網(wǎng)關(guān)服務(wù),進行簡單的前端數(shù)據(jù)校驗、協(xié)議以及格式轉(zhuǎn)換適配等工作。被動適配主要面向后端基礎(chǔ)資源(如數(shù)據(jù)庫、緩存等),通過依賴反轉(zhuǎn)為應(yīng)用層和領(lǐng)域?qū)犹峁?shù)據(jù)持久化和數(shù)據(jù)訪問支持,實現(xiàn)資源層的解耦。

應(yīng)用層代碼:本層代碼主要通過調(diào)用領(lǐng)域?qū)臃?wù)或其他中臺應(yīng)用層服務(wù),完成服務(wù)組合和編排形成粗粒度的服務(wù),為前臺提供 API 服務(wù)。本層代碼可進行業(yè)務(wù)邏輯數(shù)據(jù)的校驗、權(quán)限認證、服務(wù)組合和編排、分布式事務(wù)管理等工作。

領(lǐng)域?qū)哟a:本層代碼主要實現(xiàn)核心的業(yè)務(wù)領(lǐng)域邏輯,需要做好領(lǐng)域代碼的分層以及聚合之間代碼的邏輯隔離。相關(guān)的開發(fā)方法請查閱 DDD 戰(zhàn)術(shù)設(shè)計相關(guān)資料,并遵循相關(guān)設(shè)計和開發(fā)規(guī)范。

image

代碼邏輯分層和結(jié)構(gòu)

對代碼進行邏輯隔離和分層的主要意義在于:

1、避免各層代碼的交叉,保持領(lǐng)域代碼的純潔,保證中臺領(lǐng)域?qū)訕I(yè)務(wù)邏輯的穩(wěn)定。

2、業(yè)務(wù)和代碼模型的邏輯保持一致,有利于微服務(wù)的拆分和組合。

微服務(wù)的設(shè)計和拆分

微服務(wù)拆分方法

絞殺者模式

絞殺者模式類似建筑拆遷,在新建筑分階段建設(shè)完成入住后,分步拆除舊建筑物。

“絞殺者模式”是在遺留系統(tǒng)外圍,將新功能用新的方式構(gòu)建為新的服務(wù) 。通過在新的應(yīng)?中實現(xiàn)新特性,保持和現(xiàn)有系統(tǒng)的松耦合,隨著時間的推移,新的服務(wù)逐漸“絞殺”老的系統(tǒng)。以此逐步地替換原有系統(tǒng)。 對于那些老舊龐大難以更改的遺留系統(tǒng),推薦采用絞殺者模式。

修繕者模式

修繕者模式類似文物修復(fù),將存在問題的部分建筑重建或者修復(fù)后,重新加入到原有的建筑中,保持建筑原貌。

“修繕者模式”是在既有系統(tǒng)的基礎(chǔ)上,通過剝離新業(yè)務(wù)和功能,逐步“釋放”現(xiàn)有系統(tǒng)耦合度,解決遺留系統(tǒng)質(zhì)量不穩(wěn)定和 Bug 多的問題。就如修房或修路一樣,將老舊待修繕的部分進行隔離,用新的方式對其進行單獨修復(fù)。 修復(fù)的同時,需保證與其他部分仍能協(xié)同功能。 修繕模式適用于需求變更頻率不高的存量系統(tǒng)。

微服務(wù)拆分原則

微服務(wù)拆分過程中需嚴格遵守高內(nèi)聚、低耦合原則,同時結(jié)合項目的實際情況,綜合考慮業(yè)務(wù)領(lǐng)域、功能穩(wěn)定性、應(yīng)用性能、團隊以及技術(shù)等因素。

1、基于業(yè)務(wù)領(lǐng)域拆分,在領(lǐng)域模型設(shè)計時需對齊限界上下?,圍繞業(yè)務(wù)領(lǐng)域按職責(zé)單一性、功能完整性進行拆分,避免過度拆分造成跨微服務(wù)的頻繁調(diào)用。

2、基于業(yè)務(wù)變化頻率和業(yè)務(wù)關(guān)聯(lián)拆分,識別系統(tǒng)中的業(yè)務(wù)需求變動較頻繁的功能,考慮業(yè)務(wù)變更頻率與相關(guān)度,并對其進行拆分,降低敏態(tài)業(yè)務(wù)功能對穩(wěn)態(tài)業(yè)務(wù)功能的影響。

3、基于應(yīng)用性能拆分,考慮系統(tǒng)?功能性需求,識別系統(tǒng)中性能壓力較大的模塊,并優(yōu)先對其進行拆分,提升整體性能,縮小潛在性能瓶頸模塊的影響范圍。

4、基于組織架構(gòu)和團隊規(guī)模,提高團隊溝通效率。

5、基于軟件包大小,軟件包過大,不利用微服務(wù)的彈性伸縮。

6、基于不同功能的技術(shù)和架構(gòu)異構(gòu)以及系統(tǒng)復(fù)雜度。

分布式架構(gòu)設(shè)計的關(guān)注點

企業(yè)一旦采用分布式架構(gòu)和微服務(wù)技術(shù)體系,在設(shè)計時需要關(guān)注商業(yè)模式、業(yè)務(wù)邊界、數(shù)據(jù)體系、微服務(wù)設(shè)計、前臺交互以及多活容災(zāi)等多領(lǐng)域的協(xié)同。

1、數(shù)據(jù)是本難念的經(jīng)

分布式架構(gòu)下數(shù)據(jù)面臨的問題遠比集中式架構(gòu)復(fù)雜。諸如:分布式數(shù)據(jù)庫的選型、數(shù)據(jù)的分庫和分表、數(shù)據(jù)的同步與異步、跨庫和聯(lián)表查詢、數(shù)據(jù)的分布與集中、在線業(yè)務(wù)數(shù)據(jù)與統(tǒng)計分析數(shù)據(jù)的協(xié)同、集中式數(shù)據(jù)庫向分布式數(shù)據(jù)庫的遷移以及面向場景的集中數(shù)據(jù)復(fù)制等。

(1)分布式數(shù)據(jù)庫的選擇

從集中式架構(gòu)向分布式架構(gòu)轉(zhuǎn)型,第一步就需要考慮選擇什么樣的分布式數(shù)據(jù)庫。

為解決交易型分布式數(shù)據(jù)庫的橫向計算能力,目前主要有三種類型的分布式數(shù)據(jù)庫:一體化交易型分布式數(shù)據(jù)庫方案(如阿里 OceanBase 和華為高斯數(shù)據(jù)庫,多采用 Paxos 協(xié)議實現(xiàn)多副本數(shù)據(jù)一致)、單機交易數(shù)據(jù)庫加數(shù)據(jù)庫中間件方案(如騰訊 TDSQL 和 TBase 等,多采用數(shù)據(jù)同步實現(xiàn)多副本數(shù)據(jù)一致)和單機交易數(shù)據(jù)庫加分庫基礎(chǔ)類庫(如 ShardingSphere 等,主要實現(xiàn)數(shù)據(jù)路由和歸集)方案。三者的使用場景基本相同,都是通過對大表數(shù)據(jù)作水平切分,業(yè)務(wù)請求動態(tài)路由到指定節(jié)點,以此達到計算能力的線性擴展。一體化方案是以數(shù)據(jù)庫和中間件一體化產(chǎn)品的形式解決線性擴展問題,支持多副本,高可用,提供統(tǒng)一的運維界面。 數(shù)據(jù)庫中間件方案是以獨立數(shù)據(jù)庫中間件結(jié)合集中式數(shù)據(jù)庫的方式來解決線性擴展問題,高可用功能由中間件和數(shù)據(jù)庫自身功能分別保證。分庫基礎(chǔ)類庫方案是一種類似中間件的輕量級解決方案,適合簡單快速的交易操作,在強一致性和聚合分析查詢方面較弱。

(2)數(shù)據(jù)的分庫和分庫主鍵

選擇完分布式數(shù)據(jù)庫后,第二步就需要考慮如何按照領(lǐng)域模型和微服務(wù)進行數(shù)據(jù)庫的分庫設(shè)計,選擇合適的分庫主鍵將是一個關(guān)鍵技術(shù)點。

對于與客戶接觸的業(yè)務(wù)領(lǐng)域,個人認為可以以客戶維度作為數(shù)據(jù)分庫主鍵,以客戶為實體,確保所有與本客戶接觸和服務(wù)的數(shù)據(jù)都在一個單元內(nèi),通過集中共享的中臺服務(wù),為所有渠道的客戶提供一致性體驗。如果后序管理流程需要基于區(qū)域管理要求,也可以考慮在后序業(yè)務(wù)環(huán)節(jié)的數(shù)據(jù)庫中以區(qū)域維度作為數(shù)據(jù)庫分庫主鍵,滿足業(yè)務(wù)基于區(qū)域的管理要求。

如何將客戶維度的數(shù)據(jù)傳輸?shù)揭詤^(qū)域為維度的數(shù)據(jù)庫中?我們可以考慮基于消息隊列的事件驅(qū)動模型。

系統(tǒng)如果做不到“以客戶為中心”,又如何能實現(xiàn)“以客戶為中心”的業(yè)務(wù)需求呢?

(3)高頻熱點數(shù)據(jù)的緩存

對于像產(chǎn)品基礎(chǔ)數(shù)據(jù)、主數(shù)據(jù)之類的熱點高頻訪問數(shù)據(jù),在進行系統(tǒng)設(shè)計時需考慮將這些數(shù)據(jù)加載至緩存中,降低數(shù)據(jù)庫的壓力,對外提供高性能的數(shù)據(jù)訪問能力。

緩存技術(shù)的使用就像調(diào)味料一樣,投入小見效快,用戶體驗提升快。

(4)數(shù)據(jù)副本與跨庫聯(lián)表查詢

采用分布式技術(shù)后,數(shù)據(jù)將碎片化,為了減輕由于跨庫以及聯(lián)表查詢給分布式數(shù)據(jù)庫的壓力,需要建立多維的全局數(shù)據(jù)視圖(如客戶統(tǒng)一視圖、業(yè)務(wù)統(tǒng)計數(shù)據(jù)視圖等)和面向具體場景的預(yù)處理好的數(shù)據(jù)聚合副本,提供復(fù)雜場景的數(shù)據(jù)查詢服務(wù),減輕交易型數(shù)據(jù)庫的壓力。

全局數(shù)據(jù)視圖其數(shù)據(jù)來源于各業(yè)務(wù)條線的分布式數(shù)據(jù)庫,從源端分布式數(shù)據(jù)庫通過準實時的方式匯集(可以基于數(shù)據(jù)庫日志捕獲技術(shù)加消息隊列)。全局視圖的數(shù)據(jù)庫也可以是分布式數(shù)據(jù)庫,根據(jù)業(yè)務(wù)要求選擇合適的分庫主鍵進行數(shù)據(jù)重分布。

對于分布式數(shù)據(jù)庫跨庫關(guān)聯(lián)查詢性能低的問題,有兩種解決方案,根據(jù)具體場景采用合適的方案:

1)面向場景的數(shù)據(jù)副本查詢庫。將這些需要關(guān)聯(lián)查詢的數(shù)據(jù)副本集中存放在一個分布式數(shù)據(jù)庫中。在進行數(shù)據(jù)匯集時,提前做好數(shù)據(jù)關(guān)聯(lián)處理(如多表數(shù)據(jù)合并成一個寬表),通過查詢微服務(wù),專職提供關(guān)聯(lián)查詢服務(wù)。

2)小表廣播模式。有些業(yè)務(wù)場景中少量表(如用戶、機構(gòu)表等)需要跟業(yè)務(wù)數(shù)據(jù)進行關(guān)聯(lián)查詢,這種場景可以考慮在業(yè)務(wù)數(shù)據(jù)庫中新建一張復(fù)制表(無需全部字段,取必要字段即可),在主表發(fā)生變化時,可以通過發(fā)布訂閱的消息隊列模式刷新復(fù)制表的數(shù)據(jù),保證數(shù)據(jù)的一致性。

(5)合理的數(shù)據(jù)冗余

完成領(lǐng)域模型和微服務(wù)設(shè)計后,集中式數(shù)據(jù)庫的數(shù)據(jù)將被分散到不同微服務(wù)的分布式數(shù)據(jù)庫中。數(shù)據(jù)實體的依賴關(guān)系將被打破,如果需要調(diào)用前序或后序微服務(wù)的數(shù)據(jù)實體(如:投保微服務(wù)生成的投保單、保單管理微服務(wù)的保單需要關(guān)聯(lián)投保單,理賠的報案需要關(guān)聯(lián)保單等,或電商業(yè)務(wù)中:銷售過程中的商品、運輸過程中的貨物需要關(guān)聯(lián)商品信息),這時候就會跨庫或者跨微服務(wù)調(diào)用了,必然影響系統(tǒng)性能。

如何處理這些跨微服務(wù)的關(guān)鍵實體數(shù)據(jù)?

最好的方式就是數(shù)據(jù)冗余,將前序或后序環(huán)節(jié)的關(guān)鍵數(shù)據(jù)以數(shù)據(jù)清單復(fù)制表(只需必要的關(guān)鍵數(shù)據(jù),不需要所有明細數(shù)據(jù))的方式冗余存儲。冗余的好處是,前臺頁面可以一次性獲取本領(lǐng)域?qū)嶓w數(shù)據(jù)和關(guān)聯(lián)實體清單數(shù)據(jù),同時也可以在本庫對關(guān)聯(lián)清單數(shù)據(jù)進行查詢。只有在需要獲取關(guān)聯(lián)實體數(shù)據(jù)明細時,才調(diào)用前序或后續(xù)微服務(wù)獲取全量數(shù)據(jù)。

合理的數(shù)據(jù)冗余可以減少跨庫查詢,提升系統(tǒng)性能。

(6)如何數(shù)據(jù)遷移?

從集中式數(shù)據(jù)庫向分布式數(shù)據(jù)庫切換時,數(shù)據(jù)遷移的復(fù)雜度將大大增加。需要考慮如何進行數(shù)據(jù)遷移?現(xiàn)有技術(shù)條件下,是不是不做數(shù)據(jù)遷移也可以無縫切換?

傳統(tǒng)集中式架構(gòu)數(shù)據(jù)多集中在一個集中式數(shù)據(jù)庫中,數(shù)據(jù)關(guān)聯(lián)度高。

分布式架構(gòu)下,數(shù)據(jù)會隨著微服務(wù)而同步拆分,數(shù)據(jù)將變得碎片化,存在復(fù)制表,數(shù)據(jù)重分布,數(shù)據(jù)關(guān)聯(lián)被打破,甚至還可能需要重建數(shù)據(jù)關(guān)聯(lián)。另外,分布式架構(gòu)的容災(zāi)和多中心多活要求,數(shù)據(jù)遷移時還需要考慮數(shù)據(jù)的多副本和多中心的數(shù)據(jù)復(fù)制。分布式架構(gòu)下數(shù)據(jù)遷移的復(fù)雜度大增。

互聯(lián)網(wǎng)公司大多采用演進式架構(gòu)模式,有計劃分階段的進行技術(shù)體系的升級,很多時候用戶無感知就完成了架構(gòu)的升級。而傳統(tǒng)企業(yè)在做技術(shù)升級時如采用絞殺者重構(gòu)模式,是否必須要做數(shù)據(jù)遷移?如果不做數(shù)據(jù)遷移是否也可以順利切換?是否通過數(shù)據(jù)路由加全量數(shù)據(jù)視圖的方案就可以不做數(shù)據(jù)遷移,實現(xiàn)新舊并存,無縫切換?數(shù)據(jù)切換方案需要詳細設(shè)計和慎重考慮(尚在考慮中,且聽下回分解)。

(7)數(shù)據(jù)的異步和同步

分布式架構(gòu)下事件驅(qū)動設(shè)計模式是常用的方法,通過基于消息隊列的發(fā)布訂閱模式,可以很好的實現(xiàn)業(yè)務(wù)異步化。非實時業(yè)務(wù)場景可以采用事件驅(qū)動的模式實現(xiàn)異步化,減輕數(shù)據(jù)庫壓力。

也可以通過異步模式實現(xiàn)準實時的數(shù)據(jù)讀寫分離,提高數(shù)據(jù)庫性能。

2、中臺和微服務(wù)要處理好邊界

條條道路通羅馬,不管走哪條路,憑感覺或拍腦袋也可以設(shè)計出微服務(wù),拆分結(jié)果可能與按照 DDD 方法出來的結(jié)果類似。但是如果有好的理論和方法指導(dǎo),不但做事情有矩可循的,而且可以避免走彎路。由于 DDD 在設(shè)計的時候已經(jīng)做好了邏輯的邊界劃分,在微服務(wù)需要組合和重新拆分時也會變得容易得多。

還是有必要提一下:中臺和微服務(wù)設(shè)計可以借鑒 DDD 的設(shè)計原則和理念,不過戰(zhàn)術(shù)設(shè)計部分由于過于復(fù)雜和學(xué)習(xí)成本過高,可以參考使用。

3、前、中臺協(xié)同和前臺數(shù)據(jù)的按需加載

前臺應(yīng)用未來可能多采用單頁面(SPA)的微前端(對應(yīng)于微服務(wù)的前端展現(xiàn),一個微服務(wù)對應(yīng)一個微前端)方式,通過前端集成框架(類似門戶)實現(xiàn)多頁面組合,提供統(tǒng)一的用戶體驗,在微服務(wù)和數(shù)據(jù)庫設(shè)計時也需要協(xié)同考慮前端頁面邏輯。

為減輕跨微服務(wù)的訪問,前端頁面展示時應(yīng)以清單數(shù)據(jù)方式按需加載,后端數(shù)據(jù)設(shè)計時也應(yīng)同步考慮如何組合前端數(shù)據(jù)展示。如需要展示明細數(shù)據(jù),通過調(diào)用 API 服務(wù)的方式獲取全量數(shù)據(jù),減少不必要的跨微服務(wù)調(diào)用。

另外,符合條件的應(yīng)用也可考慮頁面的動靜分離和路由接入,將靜態(tài)頁面通過 CDN 的技術(shù),部署在靠近用戶的機房,降低交互次數(shù),減少跨廣域網(wǎng)訪問帶來的網(wǎng)絡(luò)延遲。

前端知識有限,就寫這么多了,哈哈。

4、容災(zāi)和多活的全局考慮

分布式架構(gòu)的高可用是在應(yīng)用、數(shù)據(jù)和基礎(chǔ)設(shè)施的分布式技術(shù)升級后,通過多數(shù)據(jù)中心協(xié)同來實現(xiàn)的。

為了容災(zāi)和多活,在設(shè)計方面需要考慮:1)合適的分布式數(shù)據(jù)庫。2)合理的數(shù)據(jù)分庫主鍵設(shè)計,數(shù)據(jù)的多副本和同步技術(shù)。3)單元化架構(gòu)設(shè)計,處理好通用中臺和專屬中臺的部署和依賴關(guān)系,實現(xiàn)業(yè)務(wù)的自包含,減少跨數(shù)據(jù)中心調(diào)用。4)訪問層的接入,對外部訪問進行路由、限流以及灰度發(fā)布。5)統(tǒng)一的全局配置數(shù)據(jù),每個數(shù)據(jù)中心都有實時同步的全量配置數(shù)據(jù),實現(xiàn)容災(zāi)和多活的一鍵切換。

5、避免過度拆分和硬件依賴

過度過細的微服務(wù)拆分帶來更多的軟件維護成本和運維壓力,過多的分布式事務(wù)也會帶來性能和數(shù)據(jù)一致性的壓力。在進行設(shè)計時,要在保證邏輯邊界清晰的情況下,嚴控微服務(wù)的過度拆分和采用過多的分布式事務(wù)。

分布式架構(gòu)的自動的彈性伸縮大多是通過軟件的方式去實現(xiàn)的,為保證應(yīng)用的彈性伸縮能力,在設(shè)計中應(yīng)實現(xiàn)去硬件的無中心化(如可采用軟負載,就不用 F5 之類的硬負載),盡量通過軟件實現(xiàn)彈性伸縮。因為一旦綁定硬件設(shè)備,在硬件遇到瓶頸需要自動彈性伸縮的時候,就需要人工干預(yù),無法自動彈性伸縮。

寫在最后

正如老馬說的采用微服務(wù)的企業(yè)需具備一定的高度,如文化、組織和技術(shù),DDD 同樣也需要站一定的高度。如果高度不夠,我們是否可以站在巨人的肩上呢?

在領(lǐng)域模型和微服務(wù)設(shè)計時,守住領(lǐng)域模型和邊界,各司其職,才能長治久安!

謹記:邊界!邊界!邊界!


1、聚合根、實體、值對象的區(qū)別?
從標識的角度:

聚合根具有全局的唯一標識,而實體只有在聚合內(nèi)部有唯一的本地標識,值對象沒有唯一標識,不存在這個值對象或那個值對象的說法;

從是否只讀的角度:

聚合根除了唯一標識外,其他所有狀態(tài)信息都理論上可變;實體是可變的;值對象是只讀的;

從生命周期的角度:

聚合根有獨立的生命周期,實體的生命周期從屬于其所屬的聚合,實體完全由其所屬的聚合根負責(zé)管理維護;值對象無生命周期可言,因為只是一個值;

2、聚合根、實體、值對象對象之間如何建立關(guān)聯(lián)?
聚合根到聚合根:通過ID關(guān)聯(lián);

聚合根到其內(nèi)部的實體,直接對象引用;

聚合根到值對象,直接對象引用;

實體對其他對象的引用規(guī)則:1)能引用其所屬聚合內(nèi)的聚合根、實體、值對象;2)能引用外部聚合根,但推薦以ID的方式關(guān)聯(lián),另外也可以關(guān)聯(lián)某個外部聚合內(nèi)的實體,但必須是ID關(guān)聯(lián),否則就出現(xiàn)同一個實體的引用被兩個聚合根持有,這是不允許的,一個實體的引用只能被其所屬的聚合根持有;

值對象對其他對象的引用規(guī)則:只需確保值對象是只讀的即可,推薦值對象的所有屬性都盡量是值對象;

3、如何識別聚合與聚合根?
明確含義:一個Bounded Context(界定的上下文)可能包含多個聚合,每個聚合都有一個根實體,叫做聚合根;

識別順序:先找出哪些實體可能是聚合根,再逐個分析每個聚合根的邊界,即該聚合根應(yīng)該聚合哪些實體或值對象;最后再劃分Bounded Context;

聚合邊界確定法則:根據(jù)不變性約束規(guī)則(Invariant)。不變性規(guī)則有兩類:1)聚合邊界內(nèi)必須具有哪些信息,如果沒有這些信息就不能稱為一個有效的聚合;2)聚合內(nèi)的某些對象的狀態(tài)必須滿足某個業(yè)務(wù)規(guī)則;

1.一個聚合只有一個聚合根,聚合根是可以獨立存在的,聚合中其他實體或值對象依賴與聚合根。

2.只有聚合根才能被外部訪問到,聚合根維護聚合的內(nèi)部一致性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,003評論 2 374

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