通過領(lǐng)域驅(qū)動建模來劃分微服務(wù),真正的難點(diǎn)究竟在哪里?
本文轉(zhuǎn)載自微信公眾號「人月聊IT」,作者何明璐 。轉(zhuǎn)載本文請聯(lián)系人月聊IT公眾號。
今天這篇文章主要還是進(jìn)一步對上篇文章里面沒有說清楚的地方進(jìn)一步描述。
微服務(wù)劃分
注意微服務(wù)劃分涉及到兩個(gè)關(guān)鍵部分內(nèi)容。
- 哪些業(yè)務(wù)功能應(yīng)該聚合在一起。
- 哪些數(shù)據(jù)應(yīng)該聚合在一起,Owner是該微服務(wù)。
為何我不斷在強(qiáng)調(diào)這點(diǎn)。
因?yàn)樵谖⒎?wù)之間的傳統(tǒng)單體應(yīng)用階段,也有架構(gòu)設(shè)計(jì),也有組件劃分。但是一般來說只是應(yīng)用層業(yè)務(wù)功能如何聚合,而不會涉及到數(shù)據(jù)庫本身也要拆分的問題。
而微服務(wù)下的劃分必須是數(shù)據(jù)和功能全部拆分開。
微服務(wù)A只能對自己Owner的數(shù)據(jù)庫進(jìn)行類似JDBC等直接連接操作,而對于其它微服務(wù)的數(shù)據(jù)庫只能夠通過應(yīng)用層暴露的API接口服務(wù)進(jìn)行。
如上圖中紅叉的地方是不允許的。
傳統(tǒng)單體應(yīng)用架構(gòu)設(shè)計(jì),雖然也進(jìn)行了組件劃分,但是一般數(shù)據(jù)庫沒有拆分。組件雖然也可以單獨(dú)找APP Server獨(dú)立部署,但是底層數(shù)據(jù)庫還是耦合在一起的。
因此單個(gè)組件并沒有實(shí)現(xiàn)從數(shù)據(jù)庫到應(yīng)用層的完全解耦。
雖然組件設(shè)計(jì)開發(fā)規(guī)范要求組件A需要通過API訪問組件B提供的能力,但是實(shí)際大部分時(shí)候組件A是直接在訪問后臺數(shù)據(jù)庫表。
因?yàn)檫@樣做開發(fā)實(shí)現(xiàn)起來最簡單。
所有很多規(guī)范約束實(shí)際很難真正落地,組件化后仍然是緊耦合狀態(tài)。
因此微服務(wù)劃分同時(shí)需要考慮功能和數(shù)據(jù)兩個(gè)層面的聚合和拆分,這個(gè)劃分實(shí)際比傳統(tǒng)架構(gòu)設(shè)計(jì)中的組件劃分更難,考慮的點(diǎn)也需要更多。
領(lǐng)域模型來劃分微服務(wù)
領(lǐng)域驅(qū)動設(shè)計(jì)概念來源于2004年著名建模專家Eric Evans,也出了一本經(jīng)典的領(lǐng)域驅(qū)動設(shè)計(jì)的建模書籍。但是DDD本身多年前發(fā)展并不是很火。
如果回到10多年前,主流的架構(gòu)設(shè)計(jì)思路偏RUP+UML,而DDD建模思路實(shí)際更加復(fù)雜,能夠應(yīng)用好本身不容易。其次,很多IT項(xiàng)目本身也不需要用到這么復(fù)雜的建模方法。
而最近5年,隨著微服務(wù)架構(gòu)的流行,DDD領(lǐng)域建模再次受到重視。
一個(gè)核心應(yīng)用點(diǎn)就在于微服務(wù)究竟應(yīng)該如何劃分?
領(lǐng)域驅(qū)動設(shè)計(jì)中的限界上下文,子域劃分給了很好的思路和方法。比如常用的基于事件風(fēng)暴來劃分上下文。
識別事件和命令
事件你可以理解為一個(gè)事物所編寫出來的最終狀態(tài),例如訂單已創(chuàng)建,支付已完成,商品已發(fā)貨等即是關(guān)鍵的事件。而命令可以理解為具體的業(yè)務(wù)功能或操作,比如創(chuàng)建訂單,檢索商品,扣減庫存等。
可以看到事件和命令的識別和分析中,都可以識別到具體的領(lǐng)域?qū)ο蟆?/p>
基于領(lǐng)域的對象進(jìn)行聚合
如何進(jìn)行聚合?簡單來說仍然是通過識別和梳理出來的共性領(lǐng)域?qū)ο筮M(jìn)行聚合。將對同一領(lǐng)域?qū)ο蟮乃忻詈褪录季酆显谝黄稹?/p>
進(jìn)行上下文邊界的劃分
基于聚合完成的情況進(jìn)行上下文邊界的劃分,這里實(shí)際上不同的領(lǐng)域?qū)ο笕绻麑儆谕粋€(gè)大類的業(yè)務(wù)場景,仍然是可以劃分到一個(gè)上下文里面的。
也就是不是簡單地按聚合完成的領(lǐng)域?qū)ο髣澐稚舷挛倪吔?,很多和核心領(lǐng)域?qū)ο笙嚓P(guān)的附屬對象也需要劃分到同一個(gè)上下文的。
比如電商里面的訂單是一個(gè)大量的領(lǐng)域?qū)ο?,可以劃分為?dú)立的上下文,但是對應(yīng)的購物車也是我們識別的對象,購物車本身同樣屬于產(chǎn)品訂購場景,訂單的擴(kuò)展附屬對象,因此需要將購物車也劃分到訂單上下文里面。
再次總結(jié)下整個(gè)方法思路如下。
即仍然是業(yè)務(wù)場景驅(qū)動分析加頭腦風(fēng)暴,識別出關(guān)鍵的業(yè)務(wù)事件(如果采用Scrum方法,這個(gè)也可以理解為獨(dú)立的UserStory)。任何一個(gè)業(yè)務(wù)事件都包括了業(yè)務(wù)動作,業(yè)務(wù)對象和對象狀態(tài)三要素。
注意,常用的事件風(fēng)暴法里面更加強(qiáng)調(diào)先找到狹義的事件,即對象狀態(tài)。比如已發(fā)貨,已付款,已凍結(jié)等狀態(tài)事件,然后再去分析狀態(tài)事件對應(yīng)的對象和業(yè)務(wù)操作。在你不熟悉某個(gè)領(lǐng)域的時(shí)候大可不必按部就班的這樣做,直接通過業(yè)務(wù)場景和流程梳理出核心的細(xì)粒度業(yè)務(wù)活動點(diǎn)即可。業(yè)務(wù)活動上自然會附著有對象和狀態(tài)。
將事件分解為這三要素后,再次按業(yè)務(wù)對象進(jìn)行聚類。比如訂單是一個(gè)業(yè)務(wù)對象,將所有和訂單相關(guān)的業(yè)務(wù)事件全部聚合在一起。
在聚類完成后,每一個(gè)聚類就是一個(gè)獨(dú)立的限界上下文。
聚合,限界上下文和子域
如果你按上面步驟將每個(gè)聚合劃分為一個(gè)個(gè)獨(dú)立的微服務(wù)。
那么最終的微服務(wù)一定粒度太細(xì)。
因?yàn)槊恳粋€(gè)核心的業(yè)務(wù)對象都變成了一個(gè)微服務(wù)。
拿一個(gè)傳統(tǒng)的供應(yīng)鏈系統(tǒng)來舉例,你會看到采購采購單,采購訂單,物料,供應(yīng)商,框架協(xié)議,入庫單,出庫單,庫存信息,采購計(jì)劃等是核心的功能聚合點(diǎn)。那么一個(gè)傳統(tǒng)的供應(yīng)鏈系統(tǒng)需要劃分出好幾十個(gè)微服務(wù)?
按事件風(fēng)暴梳理事件并基于對象進(jìn)行功能聚合本身沒有錯。
但是每個(gè)聚合本身不能完全1對1映射到微服務(wù)上面。
因此應(yīng)該是將多個(gè)相關(guān)的聚合歸并到一起,并以此來劃分限界上下文。限界上下文可以只包含一個(gè)聚合,也可以包含多個(gè)聚合。
那么子域和限界上下文什么關(guān)系?
一般來講子域和限界上下文之間是對應(yīng)的關(guān)系,但是子域本身也可能是將多個(gè)限界上下文合并在一起,形成一個(gè)大的子域。
但是不論什么情況。都應(yīng)該意識到不能按單獨(dú)的一個(gè)個(gè)聚合來劃分微服務(wù)。
真正的問題關(guān)鍵不是聚合,而是如何將多個(gè)聚合劃分到一個(gè)限界上下文里面。
問題關(guān)鍵-如何將多個(gè)聚合劃分到一個(gè)上下文或子域
在我自己以前做企業(yè)IT架構(gòu)規(guī)劃的時(shí)候,在進(jìn)行業(yè)務(wù)架構(gòu)規(guī)劃的時(shí)候就經(jīng)常會遇到業(yè)務(wù)域劃分的問題。
其中一個(gè)常用的方法就是參考業(yè)界常用的模型,比如供應(yīng)鏈領(lǐng)域有標(biāo)準(zhǔn)的Scor模型可以參考。整個(gè)企業(yè)完整業(yè)務(wù)你也可以參考價(jià)值鏈模型。如果你是做電信行業(yè)項(xiàng)目,則有標(biāo)準(zhǔn)的eTom模型可以參考。而產(chǎn)品研發(fā)則完全可以參考IPD模型。
所以參考業(yè)界標(biāo)準(zhǔn)模型是常用的一個(gè)劃分業(yè)務(wù)域的方法。
其次,IT人員做架構(gòu)一定不能脫離企業(yè)真實(shí)的業(yè)務(wù)組織架構(gòu)和業(yè)務(wù)運(yùn)作。
還是拿采購類業(yè)務(wù)來說,一個(gè)小點(diǎn)的企業(yè)就一個(gè)部門供應(yīng)鏈部,會將所有招投標(biāo),供應(yīng)商管理,采購詢價(jià),采購,采購執(zhí)行,庫存管理等一系列事情關(guān)閉搞定。但是對于大集團(tuán)型企業(yè),往往招投標(biāo),采購,物流,供應(yīng)商管理等都是獨(dú)立的部門。至少也是獨(dú)立的科室。
為啥要去觀察企業(yè)本身業(yè)務(wù)組織劃分?
我們實(shí)際來觀察下你會發(fā)現(xiàn)。
拿一個(gè)企業(yè)來說,這個(gè)企業(yè)的采購部內(nèi)部可能經(jīng)常開會或吵架,但是采購部和招投標(biāo)部之間經(jīng)常就是一些招投標(biāo)結(jié)果信息的傳遞。也就是企業(yè)在劃分業(yè)務(wù)部門或組織的時(shí)候,本身就在考慮如何減少跨部門的交互接口。
這和微服務(wù)拆分里面的微服務(wù)間要盡量解耦是一個(gè)道理。
所以再回到多個(gè)聚合如何進(jìn)一步歸類的問題也是同樣道理。
當(dāng)你觀察的時(shí)候,你會發(fā)現(xiàn)供應(yīng)商,物料的管理實(shí)際是同一個(gè)基礎(chǔ)數(shù)據(jù)管理部門在做。那么這兩個(gè)聚合完全是可以劃分到一個(gè)限界上下文里面。類似還有采購請購,采購訂單,采購執(zhí)行都是一個(gè)部門在做,那么也可以歸到一個(gè)上下文。
當(dāng)然你可以從一個(gè)完整業(yè)務(wù)流程分析出發(fā),看究竟分為哪些大的階段,一般來說這些階段點(diǎn)都可以作為微服務(wù)劃分的邊界點(diǎn)。
往往比單純的事件風(fēng)暴后,再聚合更加有效。比如電商核心業(yè)務(wù)場景和訂單生命周期如下。
當(dāng)你對核心領(lǐng)域?qū)ο蟮纳芷陔A段梳理清楚后,實(shí)際上已經(jīng)可以明確地識別出關(guān)鍵的上下文邊界和關(guān)鍵的領(lǐng)域?qū)ο?。比如上圖可以看到商品,訂單,供應(yīng)商,用戶,庫存都是關(guān)鍵的領(lǐng)域?qū)ο?,相關(guān)的業(yè)務(wù)操作也圍繞這些業(yè)務(wù)對象展開。
進(jìn)一步思考,從聚合到上下文和子域
當(dāng)談到這里的時(shí)候,自然出現(xiàn)了一個(gè)新疑惑。
即采用傳統(tǒng)的按業(yè)界參考模型,大的業(yè)務(wù)流程階段邊界,或者企業(yè)本身的業(yè)務(wù)組織劃分等來劃分微服務(wù),這樣劃分出來的微服務(wù)粒度更加合適,也更加容易滿足高內(nèi)聚,松耦合的需求。那么為何還要采用領(lǐng)域建模去劃分微服務(wù)?
因此從聚合到上下文,這個(gè)邏輯如何從技術(shù)上去走通成了問題的關(guān)鍵。
即各個(gè)聚合之間本身還有耦合性,我如何去分析哪些聚合應(yīng)該進(jìn)一步歸類到一個(gè)限界上下文里面,這有無一種技術(shù)層面的分析方法?
在這里談下我個(gè)人的一些思考。
基于核心業(yè)務(wù)活動進(jìn)行聚合
核心業(yè)務(wù)活動一般指最終帶來明確業(yè)務(wù)價(jià)值的活動。
舉例來說采購訂單是核心業(yè)務(wù)活動,而采購申請,框架協(xié)議等是輔助類活動,或前導(dǎo)類活動,最終目的都是形成可執(zhí)行的采購訂單。
付款是核心業(yè)務(wù)活動,而付款申請則是輔助活動,最終為付款服務(wù)。
基于這個(gè)思路就可以將諸多的輔助業(yè)務(wù)活動,支撐活動等圍繞核心業(yè)務(wù)活動的多個(gè)聚合進(jìn)行歸類,形成上下文邊界。
基于上層業(yè)務(wù)分析底層聚合之間的耦合性
拿供應(yīng)商和物料兩個(gè)聚合來說,為何建議放到一個(gè)上下文和微服務(wù)里面。
如上圖,在分析業(yè)務(wù)場景的時(shí)候你會發(fā)現(xiàn),對于其它聚合根和供應(yīng)商,物料之間的依賴關(guān)系不是點(diǎn)對點(diǎn)的依賴,而是需要供應(yīng)商和物料聚合后的信息。
供應(yīng)商和物料兩個(gè)對象本身沒有耦合性。
當(dāng)時(shí)上層的業(yè)務(wù)場景讓兩個(gè)對象之間產(chǎn)生了明顯的耦合性。在這種情況下就需要將其劃分到一個(gè)限界上下文里面去。
由于在一個(gè)上下文,后續(xù)微服務(wù)劃分對應(yīng)一個(gè)數(shù)據(jù)庫,自然也就減少了上層的多次API接口服務(wù)調(diào)用后的應(yīng)用層組裝操作。
希望以上兩點(diǎn)思路對你在劃分上下文邊界的時(shí)候有所幫助。
再次總結(jié)下本文想說明的一些關(guān)鍵內(nèi)容如下。
1.微服務(wù)設(shè)計(jì)到功能和數(shù)據(jù)兩個(gè)層面的劃分
2.事件風(fēng)暴到聚合,多個(gè)聚合如何劃分到一個(gè)上下文是難點(diǎn)
3.基于核心業(yè)務(wù)活動和聚合間耦合性是常用方法
當(dāng)然,這篇文章還有很多沒有思考完善的地方,也歡迎大家提出不同意見并進(jìn)一步探討,方便我后續(xù)對微服務(wù)劃分思路進(jìn)一步優(yōu)化改進(jìn)。