本文是我學習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,《領域驅動設計模式、原理與實踐》
在同事的推薦下,開始學習張逸老師的《領域驅動戰略設計實踐》課程,配合《領域驅動設計模式、原理與實踐》一書共同學習DDD,感覺會比單看書好很多,也在此推薦一下張逸老師的這門課,五月份馬上會出《領域驅動戰術設計實踐》,值得期待。