1776 年亞當斯密發表《國富論》,標志著經濟學的誕生。2004 年,一本名為《領域驅動設計·軟體核心複雜性應對之道》的書問世,開闢了軟體開發的一個新流派:領域驅動設計。看完這本書,十個人有九個人的感覺都是:似懂非懂,若有所得,掩卷長思,一無所得,我個人的感覺同樣如此。出於興趣,多年來仔細研讀了幾十... ...
經過多年的研究與思考,實踐與總結,本人逐漸對 DDD 有所領悟,本文以一個較短的篇幅,提綱挈領地梳理出 DDD 的核心脈絡,希望與各位做一探討。
1776 年亞當斯密發表《國富論》,標志著經濟學的誕生。2004 年,一本名為《領域驅動設計·軟體核心複雜性應對之道》的書問世,開闢了軟體開發的一個新流派:領域驅動設計。看完這本書,十個人有九個人的感覺都是:似懂非懂,若有所得,掩卷長思,一無所得,我個人的感覺同樣如此。出於興趣,多年來仔細研讀了幾十本相關書籍,融匯貫通,逐步形成了自己的一套看法,本文就和各位分享一下。
馮友蘭治哲學,提出“照著講”和“接著講”的方法論。近兩年,我斷斷續續梳理出關於領域驅動設計的兩個 PPT:《領域驅動設計》和《領域驅動設計四論》。前者的內容主要是關於 DDD 經典著作的讀書筆記,可視為照著講,以證明自己學有所本,講的不是野狐禪;後者則是在繼承的基礎上所做的創新闡釋,可視為接著講,發前人之未發。本文重點圍繞《四論》展開,從四個方面梳理出 DDD 的整個邏輯脈絡。
曾見郭象註莊子,卻是莊子註郭象。一些領域驅動設計的擁躉們,如果看到本文的論述和自己的理解相左,絲毫不用奇怪,本文闡述的四論,不是我註六經,而是六經註我。
本文不准備長篇大論,只是提綱挈領地梳理出 DDD 的核心脈絡。
DDD 四論
在系統開發這一行摸爬滾打多年,開發過眾多業務系統,也接觸過各種技術流派,結合眾多書籍,從技術風格上分為兩派:
-
江湖派:重實踐,重個人領悟,以思想體系見長
-
學院派:重規範,重系統訓練,以方法體系見長
在技術社區里,熱鬧程度可謂冰火兩重天,江湖一派熱熱鬧鬧,追隨者眾,耳濡目染,無師自通;學院一派則是養在深閨人未識,門可羅雀,亟待挖掘。
有趣的是,兩派似乎從不往來,從兩派的著作來看,江湖派偶爾涉足學院派的領域,但學院派決口不提江湖派的東西。看來在技術圈子裡,同樣也是文人相輕、道不同不相為謀,各自混各自的圈子。
作為一個互聯網工程師,長期奮戰在開發第一線,對這種技術分裂深感無奈,總感覺有座巴別塔橫亘其中。結合自身長期在一線工作的經驗積累、對各種經典著作的熟稔以及對軟體開發的持久興趣,本文試圖在兩派之間架起一座橋梁,相互借鑒,熔於一爐。
從《領域驅動設計·軟體核心複雜性應對之道》(英文版)在 2004 年第一次發表算起,到現在已有近 20 年,期間闡述 DDD 的書汗牛充棟,但 DDD 具體包含哪些內容,似乎從沒有達成共識,有種包羅萬象的感覺。本文圍繞 DDD 的經典著作,參照學院派的風格,將 DDD 概括為四部分:
- 結構論:戰術建模與戰略建模
- *過程論:系統重構與軟體工程
- 語言論:基於模型的統一語言
- 建模論:模型驅動的領域建模
這個劃分,其實體現了本人長期的實踐體悟和理論思考,並不是隨意為之。王陽明晚年將其畢生所學,濃縮為四句話:
- 無善無噁心之體,
- 有善有惡意之動,
- 知善知惡是良知,
- 為善去惡是格物。
本人將 DDD 概括為四論,不是要蓋棺定論,只是希望去掉枝葉保留主幹,避免 DDD 逐漸淪為一種坊間流傳的野狐禪。
繞不開的複雜度
複雜度
《沒有銀彈:軟體工程的本質性與附屬性工作》(No Silver Bullet—Essence and Accidents of Software Engineering),1986 年發表一篇關於軟體工程的經典論文,打開了複雜度這個潘多拉魔盒。論文中 brooks 把失控的、複雜的軟體項目比作中世紀的狼人,只有銀彈才能殺死它。但是由於軟體開發的本質複雜性,使得真正的銀彈並不存在,即沒有任何技術或管理上的進展, 能夠獨立地許諾十年內使軟體系統項目生產率、 可靠性或簡潔性獲得數量級上的進步。
到現在幾十年過去了,狼人依舊未被殺死,人月依舊是個神話,銀彈在哪?
複雜度也許永遠不能消除,但我們可以分析複雜度,進而管理複雜度。在軟體開發領域,大體上可以將複雜度分為三類:軟體本身固有的複雜度,業務邏輯帶來的複雜度,以及技術複雜度。
應對之道
從我看過的幾本經典書籍來看,軟體核心複雜性的應對之道,不外乎就是:抓住總體,理清局部。
DDD 與複雜度
DDD 提供了一些應對複雜度的具體方法:
-
通過架構設計來分離業務複雜度和技術複雜度
-
通過限界上下文將一個大系統切分為若幹高內聚低耦合的子領域
-
通過領域模型對業務領域的知識進行抽象
什麼是領域
Domain-Driven Design 還是 Model-Driven Design ?
在《領域驅動設計·軟體核心複雜性應對之道》這本書里,對什麼是“領域”,只有簡單的一句話:“每個軟體程式是為了執行用戶的某項活動,或是滿足用戶的某種需求。這些用戶應用軟體的問題區域就是軟體的領域。” 除此之外,講的更多的是“模型” 或者“領域模型”,縱觀全書,“Model-Driven Design” 是其核心概念之一。某種程度上,把“領域驅動設計”改為“模型驅動設計”,一點問題都沒有,甚至“模型驅動設計”更能體現整本書的精髓。
領域劃分與領域模型
總的來說,“領域”這個詞可能承載了太多含義。領域既可以表示整個業務系統,也可以表示其中的某個核心域或者支撐子域。在本書中,我將儘可能地區分這些概念。當談及到業務系統中的某個方面時,我會使用諸如“核心域”或者“子域”以示區別。
由於“領域模型”包含了“領域”這個詞,我們可能會認為應該為整個業務系統創建一個單一的、內聚的、全功能式的模型。然而,這並不是我們使用 DDD 的目標。正好相反,在 DDD 中,一個領域被分為若幹子域,領域模型在限界上下文中完成開發。事實上,在開發一個領域模型時,我們關註的通常只是這個業務系統的某個方面。試圖創建一個全功能的領域模型是非常困難的,並且很容易導致失敗。
關於 DDD 的一個野狐禪
本人接觸領域驅動大概已有 10 年時間,直到近期才聽同事說起過“貧血模型”,然後在網上一查,原來是 martin fowler 在 2003 年發的一個 blog:AnemicDomainModel。其本意只是指出,很多人其實誤用了領域模型,把領域模型的 Model 直接等同於 MVC 的 Model,導致 model 里只有數據沒有邏輯,fowler 給這種情形取了一個名字叫“Anemic Domain Model”。
Fowler 把這種誤用概括為一種反模式 AnemicDomainModel,在流傳過程中,有人把“Anemic Domain Model”翻譯為“貧血模型”,後面又衍生出“充血模型”“失血模型”“脹血模型”,這些就都是穿鑿附會之說,跟 Martin Fowler 無關,跟領域驅動更無關係。很多 blog 都是把 DDD 跟 MVC/DAO/ORM/TDD 放在一個層次來理解,這說明完全不理解 DDD,實際上 DDD 是跟 MBSE/SysML/UML 是一個層次的概念。
在領域驅動的經典著作里,完全沒有 AnemicDomainModel 的相關提法,把 martin fowler 的說法穿鑿附會亂加引申,將領域模型簡單分類為“失血,貧血,充血,脹血”,這個大概就屬於野狐禪了,完全背離了 DDD 的本質精神。
結構論
戰略建模與戰術建模的劃分
DDD 相關經典著作,一般都將 DDD 劃分為戰略設計和戰術設計兩部分。《領域驅動設計·軟體核心複雜性應對之道》和《實現領域驅動設計》都明確有戰略設計的提法,其他兩本書則明確提出了戰略設計和戰術設計。
顧名思義,戰略設計就是巨集觀設計,即對系統整體進行建模,稱之為“戰略建模”;戰術設計則是微觀設計,即對系統的局部進行細粒度的建模,稱之為“戰術建模”。
戰略建模
戰略設計原則必須指導設計決策,以便減少各個部分之間的互相依賴,在使設計意圖更為清晰的同時而又不失去關鍵的互操作性和協同性。戰略設計原則必須把模型的重點放在捕獲系統的概念核心,也就是系統的“遠景”上。而且在完成這些目標的同時又不能為項目帶來麻煩。為了幫助實現這些目標,我們提出了戰略設計的 3 大原則:上下文、精煉和大型結構。
理解大型系統的常用方法:
-
使用隱喻,比如電動汽車其實就是一臺電腦裝了四個輪子
-
把大型系統從邏輯上切分成若幹層,分而治之
-
把大型系統提煉為一個抽象結構,例如,馮諾依曼電腦=IO+CPU+Memory
DDD 中的上下文(Context)是個讓人迷惑的詞,從一種比較寬泛的視角來看的話,Context 可以對應於 UML 的 class 或者 SysML 的 block,即 Context 可理解為是一個類或模塊。ContextMap 則對應 UML/SysML 的 Relationship。
戰略精煉:對核心域進一步萃取,過濾掉不必要的雜質,使得其方向更清晰,內容更準確、內核更精幹。
戰略精煉:相關方法可以分為三大類,即提煉出內核、進一步精煉內核、增強溝通。
戰術建模
戰術建模側重於從微觀層面對系統進行建模。DDD 提到的戰術建模方法主要是構造塊與柔性設計。
構造塊:在類、對象、組合、繼承等層次上對系統進行設計。按照 DDD 的術語,我們可以把服務、事件、實體、值對象歸類為原子塊,把資源庫、聚合、工廠歸類為組合塊。
柔性設計:列舉了一些設計原則,類似於常見的軟體設計原則,只是換了一種說法。例如通過明確概念、避免概念過載、處理副作用等做法,得到的是一個高內聚低耦合的設計。
軟體設計原則,說到底不外乎,模塊內高內聚,模塊間去耦合。下麵是學院派總結的一些原則,可以對照來看。
過程論
DDD 關於開發過程的論述
關於開發過程,《領域驅動設計·軟體核心複雜性應對之道》這本書第三部分用 6 章的內容重點論述了重構,但對於軟體工程,只是簡單提及“敏捷開發過程”。整體而言,作者更傾向於用重構等手段不斷迭代,開發人員和業務專家緊密配合,讓協作貫穿整個項目的生命周期。本小節從下麵三個方面對 DDD 的開發過程作一擴展性論述:
-
通過重構加深理解
-
敏捷開發過程與持續交付
-
引入軟體工程的規範方法
通過重構加深理解
領域驅動設計的重點在系統設計階段,但領域驅動設計同樣將重構作為重要內容。《領域驅動設計·軟體核心複雜性應對之道》這本書第三部分共 6 章的篇幅在介紹重構。看其內容,名為重構,實則仍然是設計,例如概念建模、柔性設計、分析模式、設計模式等等,基本都是一些偏巨集觀的設計內容。內容上,跟 Martin Fowler 的《重構:改善既有代碼的設計》還是有很大區別。
論重構,當首推軟體開發一代宗師 Martin Fowler 的《重構:改善既有代碼的設計》,無人能出其右。
《重構:改善既有代碼的設計》,這本書的主要內容,用一個腦圖展示如下。
從這本書開始,”代碼壞味道”成為開發領域的一個標準術語。在重構的過程中,有一些基於經驗的原則可供參考。
就重構的具體做法上,可以從函數、數據、業務邏輯三個方面來展開。
某種程度而言,軟體工程師仍然是手工業者,軟體開發仍然沒有銀彈,重構仍然是軟體在生長過程中不可或缺的調校手段。
因此,我們也不用迷信什麼銀彈,也不必忌諱什麼過度設計與設計不足,通過多次重構迭代,讓正確的設計逐步顯現。
敏捷開發與持續交付
在《領域驅動設計》出版的年代(2004),正值敏捷開發和極限編程大行其道的年代,《領域驅動設計》儘管不局限於某種固定的開發過程,但主要還是面向“敏捷開發過程”這一新體系。
二十年後的今天,敏捷開發和極限編程早已式微,但喬梁老師的著作《持續交付》給我們提供了新的視角。喬梁老師在其著作《持續交付》里梳理了軟體工程的進化史。最近兩三年,作為騰訊外聘高級管理顧問,其“價值探索-快速驗證”的持續交付 2.0 雙環模型,令人印象深刻。
引入軟體工程的規範方法
國內最新的一本著作《解構領域驅動設計》,作者試圖參考 RUP,給 DDD 建立一種類似的規範過程。
《解構領域驅動設計》出版於 2021,據瞭解作者張逸是業內知名 DDD 專家,也是《實現領域驅動設計》一書的審校。在《解構領域驅動設計》這本書里,作者試圖將 DDD 與 RUP 融合起來,提出了 DDDUP(領域驅動設計統一過程)模型,能否成功,我們拭目以待。
《解構領域驅動設計》這本書很厚,500 多頁,值得一讀,讀完能體會到作者深厚的行業經驗。
領域驅動設計統一過程(DDDUP)參考了統一過程(rationalunified process,RUP)的二維開發模型。整個過程的二維模型圖所示,橫軸代表推動領域驅動設計在構建過程中的時間,體現了過程的動態結構,構成元素主要為 3 個階段(phase),每個階段可以由多個迭代構成;縱軸表現了領域驅動設計在各個階段中執行的活動,體現了過程的靜態結構,構成元素包括工作流(workflow)和元模型(meta model)。
語言論
統一語言
UBIQUITOUS LANGUAGE,有的書翻譯為通用語言,有的書翻譯為統一語言,其核心要點為:
-
一個團隊一種語言,語言統一才能溝通
-
將領域模型作為統一語言的核心,基於模型進行溝通
業務領域的例子
業務領域有業務領域的語言,不同業務有不同語言。下麵是騰訊動漫的一個例子。
註:圖片摘自公司同事的 km 文章
技術領域的例子
以下是幾個技術領域的例子,不同層次有不同層次的語言。
在《領域驅動設計·軟體核心複雜性應對之道》這本書里,明確提到了設計模式,這可以看做比編程語言更高一個層級的語言,提高了思維的抽象層次。
《微服務架構設計模式》和《面向模式的軟體架構》在架構設計層面建立起了一套完整的模式語言,比設計模式再高一個層級。
建模論
江湖派 vs 學院派
儘管在各自的著作中,兩派是互不感冒,但在平時的工作中,通常不分派別,哪派管用用哪派。在診斷方法上,中醫只能講出望聞問切,西醫能講的東西就太多太多。在系統建模這個事情上,學院派能講的東西,無論深度廣度還是精度,都遠超江湖派。
Model 是結合點
DDD、UML 和 Sysml 的底層邏輯全都指向 Model,Model 是三種技術的結合點。
限界上下文是理解 DDD 的鑰匙
限界上下文(Bounded Context)是 DDD 關鍵概念之一,同時可能是 DDD 裡面最令人迷惑的一個概念。可以說,理解了限界上下文也就理解了 DDD。技術上,我們可以把限界上下文建模為 UML 的 Object/Class,或者是 SysML 的 Block,理解了這一點:Bounded Context = Object/Class = Block ,所有秘密迎刃而解。
有趣的是,《解構領域驅動設計》的作者,對限界上下文的理解也經歷了一個禪宗式的參悟的過程:參禪之初,看山是山,看水是水;禪有悟時,看山不是山,看水不是水;禪中徹悟,看山仍然山,看水仍然是水。
大概所有研究 DDD 的人,都會經歷類似的過程吧。從這個角度看,把一種技術方法講成玄學,需要學習的人去慢慢參悟,這很難說不是一種退步。學院一派雖然繁瑣但至少規範,而 DDD 在術語上的模棱兩可進而導致很大的解釋空間,讓學習的人經常誤入歧途,這是 DDD 的缺點之一。
UML/SysML 是重裝備
學院一派,仍在不斷精進,從多年前發展起來的 UML,到新近在其基礎上發展起來的 SysML,建模技術正在從軟體行業逐步拓展到一般技術行業。UML 強綁定於面向對象,SysML 去除了這種綁定,因而可用於更廣泛的領域。在軟體開發領域,一般學會 UML 就可以了,如果所屬業務不採用面向對象開發模式的話,SysML 則是更適合的建模工具。
下麵是一些用 UML 做的圖,總體視覺效果看著還不錯。
架起橋梁
有一段時間,天天在琢磨 DDD/UML/SysML,腦子裡各派的技術體系在翻江倒海地打架,偶然有一次把幾幅圖放在一起,突然間眼前靈光乍現,剎那間頓悟了,那一刻終於找到瞭解開密碼的鑰匙。從此困擾自己很久的 DDD 的幾個核心概念,終於理清了頭緒,完成了整個 DDD 大廈的最後一塊拼圖,感覺自己終於可以從路的這頭走到路的那頭,再從路的那頭走到路的這頭。註意下麵這張圖中左下角的表格,將 DDD/UML/SysML 之間的核心概念對應起來,三者之間的橋梁得以建立,從此一通百通。
寫在最後
DDD 自創立以來,到現在近 20 年,這期間 DDD 野蠻生長,逐漸變為一個雜亂無章的的技術叢林,一百本書有一百種講法,包羅萬象。而同樣源遠流長的 UML/SysML/RUP 等專業方法,卻逐漸式微。
DDD 為什麼這麼香?其中的奧秘在哪?就 DDD 本身而言,頂多算一種思想體系,遠未發展為一種規範方法,其解釋空間很大導致人們的發揮空間也很大,這就非常適合技術咨詢界拿來做包裝講故事。相應的,UML/SysML/RUP 等專業方法,因為嚴謹所以可發揮的空間就少,最關鍵的是,UML/SysML/RUP 等都有版權保護,各自也都推出了自己的專業認證體系,這就阻止了眾多技術咨詢公司的進入。
在中文社區里,主要是眾多咨詢公司在熱情推動 DDD,沉寂多年的 DDD 進而又重出江湖。最近新出的一些書,各種 DDD 培訓課,就其內容而言,要麼是引經據典照著書本講,要麼就是頂著 DDD 的帽子講的卻是 UML 的內容。總的來說,DDD 沒多少新東西,只是把同樣的東西,以區別於學院派的語言,重新歸納了一遍,從而看起來像是新的東西。例如,把概要設計講成戰略建模,把詳細設計講成戰術建模,沒有本質區別,聽起來高大上而已。
本人學院派出身,對學院一派的技術更熟悉也更擅長,奈何江湖一派大行其道,從耳濡目染到深入研究,歷時數載,若有所得,在方法論上認識到一點:以學院一派的知識體係為根基,用江湖一派的方法去講故事,兩派融合方可修得上乘武功。有點類似中西醫結合,以西醫打底,以中醫行走江湖,雖不中亦不遠。毛主席曾說,自己對詞的欣賞趣味是“偏於豪放,不廢婉約”,我對不同技術流派的態度是“偏於學院,不廢江湖”。
當人們都在做多的時候,我選擇做少,正本清源,理清主脈。試圖在江湖派和學院派之間架起一座溝通的橋梁,是我一直想做的事情,本文只是一個起點。
作者:larkliu
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/Four-Theories-of-Domain-Driven-Design.html