本文是我學習Scott Millett & Nick Tune編著的《領域驅動設計樣式、原理與實踐》一書的學習筆記,一共會分為4個部分如下,此文為第1部分:
領域驅動設計的原則與實踐
戰略樣式:在有界背景關係之間通訊
戰術樣式:建立有效的領域模型
有效應用程式的設計樣式
一、什麼是領域驅動設計
腦圖瀏覽:https://www.processon.com/view/5cb49b14e4b0a13c9de1042d#map
這一章主要介紹了DDD是什麼,強調DDD是一種開發思想體系,它是樣式(戰略樣式、戰術樣式)、原則和實踐的集合,可以被應用到軟體設計中以管理複雜性。
DDD並非一種樣式語言,它是專註於交付的一種協作思想體系,其中通訊起核心作用,而要高效通訊,就需要使用公共語言。
DDD會將側重點放在以下幾個方面:
-
核心領域
-
協作
-
與領域專家探討
-
實驗研究以生成更有用的模型
-
對各種背景關係的理解
更為重要的是,不要認為DDD是一套框架,DDD也不是銀彈或靈丹妙藥,不可在專案中小題大做!
下圖展示了一個演進的領域驅動設計過程:
From:張逸《領域驅動戰略設計實踐》課程
這裡摘抄一段張逸老師在《領域驅動戰略設計實踐》課程中的話:
面對客戶的業務需求,由領域專家與開發團隊展開充分的交流,經過需求分析與知識提煉,以獲得清晰的問題域。透過對問題域進行分析和建模,識別限界背景關係,利用它劃分相對獨立的領域,再透過背景關係對映建立它們之間的關係,輔以分層架構與六邊形架構劃分系統的邏輯邊界與物理邊界,界定領域與技術之間的界限。之後,進入戰術設計階段,深入到限界背景關係內對領域進行建模,並以領域模型指導程式設計與編碼實現。若在實現過程中,發現領域模型存在重覆、錯位或缺失時,再進而對已有模型進行重構,甚至重新劃分限界背景關係。
兩個不同階段的設計標的是保持一致的,它們是一個連貫的過程,彼此之間又相互指導與規範,並最終保證一個有效的領域模型和一個富有表達力的實現同時演進。
二、提煉問題域
腦圖地址:https://www.processon.com/view/5cb5e474e4b0841b84327187#map
這一章主要介紹了什麼是知識提煉,知識提煉是一個持續協作達成共識以建立有用模型的過程,而如何實踐好這個過程,介紹了一些最佳實踐:比如專註於最有意思的對話、從用例開始、提出有力的問題等等。
而對於不需要構建新模型的人來說,研究現有模型也是有技巧的,個人感觸最深的就是要真正理解意圖,也就是不要盲從於客戶的需求,因為這個需求很可能並不能真正地解決問題和創造價值,往往需要更深層次地理解隱含的願景並且能夠認識到業務到底試圖達到什麼。影響地圖和業務模型是兩個經典的實踐方法,書中的例子線上運動裝備運營商的業務模型圖也比較經典。
三、專註於核心領域
腦圖瀏覽地址:https://www.processon.com/view/5cba8957e4b059e20a0068c8#map
這一章主要介紹了核心領域,在一個大的問題空間中會同時存在很多的小問題域,而這些小問題域往往只有少部分是核心領域,其他的可能都是通用域和支撐域。核心域是我們軟體的根本競爭力所在,因此也可以說是我們編寫軟體的原因。拿一個線上拍賣網站來說,可以見下圖所示劃分了核心域、支撐域和通用域:
對於核心域,我們需要配備最好的開發人員專註於此。對於支撐域,我們可以外包開發或者配備初級開發人員,但是要確保支撐域中的模型足夠好。而對於通用域,如果可以,我們可以尋求購買現成解決方案。
四、模型驅動設計
腦圖瀏覽地址:https://www.processon.com/view/5cbaa844e4b01941c8b441d2
這一章主要介紹了模型驅動設計和通用語言的重要性,模型驅動設計是將分析模型(業務模型)系結到程式碼實現模型並確保這兩個模型保持協同並可用的過程。
模型驅動設計專註於實現以及對於初始模型可能需要修改的約束,領域驅動設計則專註於語言、協作和領域知識,他們是一個彼此互補的關係。而要實現協作,就需要使用通用語言,藉助通用語言可以將分析模型和程式碼模型系結在一起,並最終實現團隊建模。實踐UL是一個持續的過程,多個迭代後會不斷對UL進行驗證和改進,以便實現更好的協作。
由於時間和精力都有限,只有僅僅為核心域應用模型驅動設計和建立UL才能帶來最大的價值,而不需要將這些實踐應用到整個應用程式之中。
五、領域模型實現樣式
腦圖瀏覽地址:https://www.processon.com/view/5cbab6c5e4b06bcc13844497
這一章主要介紹了領域層的概念及作用,下圖展示領域層在在整個應用程式程式碼中的位置,領域層的最大作用就在於隔離領域模型的複雜性和應用程式的技術複雜性。
在領域建模時可以遵循的設計樣式,Martin Fowler在《企業應用架構樣式》一書中提出了以下幾種:
-
領域模型樣式:適用於複雜問題域,領域中的概念被封裝為資料和行為的物件
-
事務指令碼樣式:組織所有的領域邏輯來滿足業務事務或用例
-
表模組樣式:代表著以物件形式建模的資料,資料驅動
-
活動記錄樣式:類似表模組,資料驅動,關註表中的行而非表本身
-
貧血樣式:類似領域模型,不包含任何行為,純粹的一個物件狀態模型,需要一個單獨的服務類來實現行為
六、使用有界背景關係維護領域模型的完整性
腦圖瀏覽地址:https://www.processon.com/view/5cbad3dee4b09a3e45a3fbc6
通常情況下,嘗試將單個模型用於複雜問題域通常會導致程式碼變成大泥球,而且會增加團隊之間的協作成本並降低交付業務價值的效率。有界背景關係就是劃分和破除這種大模型的有效方式,一個有界背景關係就是一個語言邊界,它可以隔離模型以避免領域術語在不同背景關係中的歧義。而我們常常提到的微服務,個人感覺更像是有界背景關係的一種技術實現途徑之一,有界背景關係中具有較高的自主性,擁有從展現層、領域邏輯層再到持久化層的完整程式碼堆疊,正應對了我們的每一個微服務的應用程式,也具有較高的獨立性,擁有自己的資料庫和一套完成的垂直切片的架構樣式。
書中還提到一個重要的觀點,那就是“並非所有有界背景關係都共享相同的架構樣式”,換句話說就是可以將不同的架構樣式應用到不同的有界背景關係中。想想這年來的企業應用架構樣式的發展,已經從單一的架構風格發展為了混合式的架構風格了,就微軟的大DEMO專案eShopOnContainers而言,也具有多種架構風格(簡單的資料驅動CRUD+簡化的分層DDD等),如下圖所示:
因此,我們也不應該侷限在某一種或者兩種架構樣式上,而是應該量身應用,沒有複雜性業務邏輯的微服務,那就應該KISS(Keep It Simple & Stupid),否則就可以考慮DDD。
七、背景關係對映
腦圖瀏覽地址:https://www.processon.com/view/5cbc3240e4b0bab909613768
背景關係對映用來捕獲各個有界背景關係之間的技術與組織關係,它最大的作用就是保持模型的完整性。張逸老師在《領域驅動戰略設計實踐》課程中提到,在戰略設計階段,針對問題域,透過引入限界背景關係和背景關係對映可以對問題域進行合理的分解,識別出核心領域和子領域,並確定領域的邊界以及他們之間的關係,從而維持模型的完整性。
限界背景關係不僅侷限於對領域模型的控制,而在於分離關註點之後,使得整個背景關係可以成為獨立部署的設計單元,這就是我們非常熟悉的“微服務”的概念;而背景關係對映的諸多樣式則對應了微服務之間的協作。
八、應用程式架構
腦圖瀏覽地址:https://www.processon.com/view/5cc1cbe4e4b0841b84400fc9
這一章討論了應用程式架構、服務和客戶端,唯一記住的只有一句:“DDD不需要特殊的架構,只要是能將技術問題與業務問題分離的架構即可”。
九、團隊開始應用DDD通常會遇到的問題
腦圖瀏覽地址:https://www.processon.com/view/5cc46afbe4b08b66b9bd9513
DDD的戰術樣式雖然可以指導我們建立有效領域模型,但這並非DDD的真正價值所在。因為,DDD其實並非編碼這麼簡單,與領域專家的協作以進行知識提煉,以及在通用語言中表述的問題域達成共識才是DDD的支柱。
在現實中,團隊在應用DDD時通常會低估應用DDD的成本,應用DDD需要一個願意學習該領域的聰明專註的團隊,還需要領域專家的參與,沒有他們,團隊就無法揭示更深層的見解。
十、應用DDD的原則、實踐與樣式
腦圖瀏覽地址:https://www.processon.com/view/5cc5568be4b059e20a0bc1e1
DDD不是靈丹妙藥,更不是“銀彈”,張逸老師說道:請事先降低對領域驅動設計的不合現實的期望,要學會運用設計原則去解決問題,而非所謂的“設計規範”。更為重要的是,僅僅在需要時應用DDD原則,不要將其用作解決所有問題的工具。
總體來說,這一章比較高屋建瓴,總結性的內容偏多,但對於沒有多少實戰經驗的人來說,閱讀完不會有太深刻的印象。不過,這並不影響,後續就是戰略設計和戰術設計的部分了,相信會隨著學習的深入,再反過來看這些原則和實踐會有更多的認識。
參考資料
Scott Millett & Nick Tune,《領域驅動設計樣式、原理與實踐》
朋友會在“發現-看一看”看到你“在看”的內容