所謂“讀書破萬卷,下筆如有神”,放到DDD上面確是個難題,僅這章的破題部分就難住了我,想要做到言簡意賅但還不能口若懸河。雖自信本人面對指責應可作到寵辱不驚,但也不能依仗此而無賴的誤導別人,所以本單的內容“我妄言之,您姑且聽之”。
我有過這樣的經驗,讀書的越多後面能攫取的有用的知識越少,也許從一本書中只能獲取到一條有用的內容,可正是因為這一條內容會讓自己瞬間悟道。2015年前我曾經讀遍了市場上所有的DDD國內外相關書籍,也幾乎讀遍了能從網路上找到的各類問題。大方面都瞭解,可在日常工作中經常會因為一個小的細節讓自己不得要領。因為這一小的瓶頸,無法上升到另外的一層臺階。所以我期望個人的一些經驗能在您悟道的途中起到啟發作用。
本章我會著重解釋一下DDD的本質,也就是其到底在說些什麼。這裡面有個人的理解也有書上的理論,結合在一起,說一些白話,最重要的是掀起她那神祕的蓋頭。
一、DDD主要說明了什麼
DDD分為兩個部分:戰略與戰術。本質一點,這兩點可指導您在業務與技術兩方面應如何做來最小化需求變更導致的系統調整;再本質一點,DDD所達到的最終結果就是我們常常在物件導向中所說的:高內聚、低耦合。實際上,想要做到這兩並不拘泥於是否使用了DDD,DDD之前也有許多上佳的實踐可供使用。我們把目標進行反推:高內聚、低耦合的系統必然會讓您的系統易維護、易擴充套件。易維護與擴充套件的系統必然是在設計之初就在業務方面與技術方面進行了思考與設計,這是起點,而DDD正好服務於這個起點。下圖概括了DDD包含的內容,其實沒您想像的複雜。
二、戰略模式
1、子域
DDD的戰略模型主要關注於“子域”與“限界上下文”,這兩個都是DDD中的特有名詞。用白話去說:“子域”是一種概念模型。再直白一點,假如您對比子域說明和實際的系統部署說明,會發現兩者並不能一一對應。再比如,您以微服務架構落地一個OA系統,子服務可能包含“使用者管理”、“簽到管理”、“會議室管理”,這些是您可見的一個個的服務,也許還使用了不同的開發語言。而子域是虛的,是對一個大系統在業務上劃小後人為的賦予每一個小業務的名稱。仍然以OA系統為例,在系統未構建前將OA這個領域分成幾個“中心”,比如“審批中心”、“員工中心”、“公文中心”,叫什麼名是您自己定義的,是為了後面大家討論的時候能明白彼此在說什麼,這裡的中心即為子域;放到落地階段,“員工中心”搞不好會分成多個子系統比如“簽到管理”、“績效管理”,這些才是物理上可見的服務。
針對“子域”,此處您需要記住的是:1)子域是一種對領域的概念上的劃分,是人為的主觀行為,靈活性較高;2)子域的目的是把大的領域劃小,使用分而治之的方法來簡化業務設計時的複雜度;3)子域的劃分是變化的,隨著您對系統認識度的加深,會刪除某些子域並增加新的進來,這不是您早期的劃分有錯,而是很自然的現象。子域的劃分方式及相關細節會在下一章進行闡述。
2、限界上下文
“限界上下文(Bounded Context,為方便,後續文章中都將其簡稱為BC)”又是一個容易產生疑惑的名稱。其重點是關注於系統的物理劃分,在這裡我給出的不嚴格定義是:限界上下文泛指系統中的模組、微服務架構中的子服務、單體中的“包(Java)”或“名稱空間(C#)”。請拿出小本本記錄下來:BC是對系統的一個物理劃分,這裡存在著可見的邊界。實踐中,建議明確子域後再進行BC設計,這種方式會讓您的工作具備理論指導。
您可能會問:為什麼叫限界上下文?這東西得分成兩個詞:限界,您的領域模型的活動範圍是有界限的,不能像脫了韁的野馬一樣亂竄。那這個界限是什麼或者說要將領域模型限制在哪裡呢?答:“上下文”中!換成人話就是某個系統或某個業務場景。因此,系統設計之初“您需要畫一個圈,將領域模型限制在這個圈內保證其不可串場,這個‘圈’即為限界上下文,實現時可以是一個單獨的服務或‘包’或‘名稱空間’”。引入BC的目的是為了達到“高內聚、低耦合”這個目標。想像一下:領域模型都在自身的圈子活動,互不干擾,後續有任何變更都不會超圈,這樣的程式是否會更好維護?限界上下文的定義可以說是相當嚴謹的,但同時也比較晦澀。所以在非嚴格的場景下只需從樸素的角度理解即可。
另外,DDD還有一個詞叫“通用語言”,簡單來說就是大家在說一個東西的時候都能明白彼此所指的是什麼。比如有個流行詞叫“打卡”,如果您在和朋友聊工作考勤,這裡的打卡代表的是員工到公司時的簽到;如果您和朋友在聊某個比較火的景點或美食,這裡的打卡代表著某人曾至此一遊。因場景不同,同一個詞就會有著不同的含義。這個場景有專業人士稱其為“語境”,其實也就是“限界上下文”,所以BC又多了一層其它的作用:他給予了某個東西一個唯一的含義。實際上,“通用語言”是面向BC的,只有在BC內討論才有意義。書上常說做DDD要有事件風暴,事件風暴的主要作用除了確認子域、BC、領域模型外,設定通用語言也是其主要的目的之一。
總結一下BC的概念:1)是系統的物理劃分;2)應根據子域進行推導;3)限定了領域模型的邊界;4)BC內,領域術語都有一個明確的含義(即通用語言)。
三、戰術模式
戰術模式對於技術人員來說就比較熟悉了,比如系統架構、領域模型、分層這類純技術的內容,是DDD書籍中大講特講的部分。戰術模式更多的聚焦於BC內,這方面的內容後續我們會進行細化說明。
複雜業務場景落地時,DDD尤其推薦使用物件導向或稱之為物件驅動(ODD)的程式設計方式。實際上,DDD並不侷限於物件導向,程式導向(經典三層模式)也會被經常用到。以個人的經驗來看,兩種方式使用的比例至多五五開,ODD使用率並不高,一般都在核心域內使用。Martin Fowler認為程式導向是一種“反模式”,我倒是覺得不存在正或反的概念,適合的最好。我們知道,一個研發團隊應該以棗核形狀為最佳,即存在著一大批的中等水平的工程師。ODD落地難度較大,開發緩慢,工作量也相對較多,應只在非常複雜的、多變的業務場景下使用。從管理角度來說,讓整個團隊無差別的使用物件導向程式設計也不太適合一般企業中的一般團隊。
四、反思
有人會反駁說:DDD如果就是這點內容,為什麼我看了半天看不懂,看懂了落地又如此之難。其實原因很簡單:要構建優秀的系統,DDD指導僅僅是一方面。正常來說,系統分成內外兩面,使用者能看到的只有外面。雖然外面的工作量相對少也無法直接反映出架構的優秀度,但其是使用者直接可見的,介面不好用、不好看,給人的印象就不好。再舉個例子,做內容檢索時一般會使用Elastic Search。在這個場景下面,DDD最多能在戰略上給出指導,好不好用、快不快還在您的技術實現,這裡面已經不是怎麼建模能搞定的事情了。您要考慮的是有多少臺伺服器、如何配置等內容。
好的系統是由各類知識和各類人員匯聚而成,要包括優秀的UI設計師、專案經理、運維、測試;作為研發的您也需要對資料庫、快取、佇列、所用框架和語言等有深入的瞭解,當然了,還要有一個端正的態度。這些都需要您在日常工作和學習中積累和反思。再實際一點,您簡歷上寫精通DDD其實也說明不了什麼,面試官一般都會從多個角度不同的技術上考核您,差一點就直接槍斃。所以,請您客觀看待DDD,切莫神化,它就像您學習的如資料庫、快取、分散式系統等知識一樣,是系統建設中的一個方面而非全部。