1. 程式人生 > >領域驅動設計

領域驅動設計

代碼 包括 行為 data ech 不同的 好處 區別 權限

1.什麽是領域驅動設計(DDD:Domain Driven Design)

領域驅動設計(DDD)是一種基於模型驅動的軟件設計方式。它以領域為核心,分析領域中的問題,通過建立一個領域模型來有效的解決領域中的核心的復雜問題。領域驅動設計提出了一套核心構造塊(如聚合、實體、值對象、領域服務、領域工廠、倉儲、領域事件,等),這些構造塊是對面向對象領域建模的一些核心最佳實踐的濃縮。這些構造塊可以使得我們的設計更加標準、有序。

什麽是領域:領域本質上可以理解為就是一個問題域,只要是同一個領域,那問題域就相同。任何一個系統都會屬於某個特定的領域,比如論壇是一個領域,只要你想做一個論壇,那這個論壇的核心業務是確定的,比如都有用戶發帖、回帖等核心基本功能。比如電商平臺、普通電商系統,這種都屬於網上電商領域,只要是這個領域的系統,那都有商品瀏覽、購物車、下單、減庫存、付款交易等核心環節。所以,同一個領域的系統都具有相同的核心業務,因為他們要解決的問題的本質是類似的

什麽是驅動:領域驅動領域模型設計,領域模型驅動代碼實現。這個就和我們傳統的數據庫驅動開發的思路形成對比了。DDD中,我們總是以領域為邊界,分析領域中的核心問題(核心關註點),然後設計對應的領域模型,再通過領域模型驅動代碼實現。而像數據庫設計、持久化技術等這些都不是DDD的核心,而是外圍的東西。

什麽是設計:DDD中的設計主要指領域模型的設計。DDD是一種基於模型驅動開發的軟件開發思想,強調領域模型是整個系統的核心,領域模型也是整個系統的核心價值所在。每一個領域,都有一個對應的領域模型,領域模型能夠很好的幫我們解決復雜的業務問題。

理解領域:假設你現在打算做一個電商平臺,但是你對這個領域沒什麽了解,那你一定得先去了解下該領域內主流的電商平臺,比如淘寶、天貓、京東、亞馬遜等。這個了解的過程就是你沈澱領域知識的過程。雖然我們明確了要做一個什麽樣的系統,該系統主要解決什麽問題,但是就這樣我們還無法開始進行實際的需求分析和模型設計,我們還必須將我們的問題進行拆分,需求進行細化。要知道一個系統到底該做成什麽樣子,到底哪些是核心業務關註點,只能靠沈澱領域內的各種知識,別無他法。。

拆分領域:有時一個領域往往太復雜,涉及到的領域概念、業務規則、交互流程太多,導致我們沒辦法直接針對這個大的領域進行領域建模。所以,我們需要將領域進行拆分,本質上就是把大問題拆分為小問題,然後各個擊破的思路。然後既然把一個大的領域劃分為了多個小的領域(子域),那最關鍵的就是要理清每個子域的邊界;然後要搞清楚哪些子域是核心子域,哪些是非核心子域,哪些是公共支撐子域;然後,還要思考子域之間的聯系是什麽。拿經典的電商系統來分析,通常一個電商系統都會包含好幾個大塊,比如:

  • 會員中心:負責用戶賬號登錄、用戶信息的管理;
  • 商品中心:負責商品的展示、導航、維護;
  • 訂單中心:負責訂單的生成和生命周期管理;
  • 交易中心:負責交易相關的業務;
  • 庫存中心:負責維護商品的庫存;
  • 促銷中心:負責各種促銷活動的支持;

上面這些中心看起來很自然,因為大家對電子商務的這個領域都已經非常熟悉了,所以都沒什麽疑問,好像很自然的樣子。之所以我們覺得子域劃分很簡單,是因為我們對整個大領域非常了解了。如果我們遇到一個冷門的領域,就沒辦法這麽容易的去劃分子域了。這就需要我們先去努力理解領域內的知識。子域劃分沒有什麽技巧,這個工作沒有任何訣竅可以使用。當我們對整個領域有一定的熟悉了,了解了領域內的相關業務的本質和關系,我們就自然而然的能劃分出合理的子域了。不過並不是所有的系統都需要劃分子域的,有些系統只是解決一個小問題,這個問題不復雜,可能只有一兩個核心概念。所以,這種系統完全不需要再劃分子域。

細化子域:了領域裏的知識,也對領域進行了子域劃分。但這樣還不夠,憑這些我們還無法進行後續的領域模型設。還必須再進一步細化每個子域,進一步明確每個子域的核心關註點,即需求細化,們需要細化的方面有以下幾點:

  1. 梳理領域概念:梳理出領域內我們關註的概念、概念的關系,並統一交流詞匯,形成統一語言;
  2. 梳理業務規則:梳理出領域內我們關註的各種業務規則,DDD中叫不變性(invariants),比如唯一性規則,余額不能小於零等;
  3. 梳理業務場景:梳理出領域內的核心業務場景,比如電商平臺中的加入購物車、提交訂單、發起付款等核心業務場景;
  4. 梳理業務流程:梳理出領域內的關鍵業務流程,比如訂單處理流程,退款流程等;

從上面這4個方面,我們從領域概念、業務規則、交互場景、業務流程等維度梳理了我們到底要什麽,整理了整個系統應該具備的功能。這個工作是一個非常具有創造性和有難度的工作。我們一方面會主觀的定義我們想要什麽;另一方面,我們還會思考我們要的東西的合理性。

關於領域概念的梳理,我覺得可以采用四色原型分析法,這個分析法通過系統的方法,將概念劃分為不同的種類,為不同種類的概念標註不同的顏色。然後將這些概念有機的組合起來,從而讓我們可以清晰的分析出概念和概念之間的關系。有興趣的同學可以在網上搜索下四色原型

2.DDD中的一些定義:

領域:用戶會把軟件程序應用於某個主體區域,這個區域就是軟件的領域。簡單來說,就認為是公司的某塊業務好了。如果領域比較大,可以將其拆分為多個子域,子域包含核心域和支撐子域,核心域顧名思義,是最重要的子域,我們應該把關註點集中在它上面;其余的子域都是支撐子域。支撐子域裏有一類特殊的用於解決通用問題的子域,稱為通用子域,例如用戶和權限等。不過這些都是相對而言的,對於消費方來說,他的支撐子域有可能就是你的核心域。個別子域可能會有交集,稱為共享內核,目的是減少重復,但是仍保持兩個獨立的上下文。由於不同子域的開發團隊可能會同時修改共享內核,所以需要小心並註意溝通。

限界上下文: 通用語言裏,同一個名詞在不同的場景裏不一定有相同的意思。比如用戶,在推薦好友(可能關註年齡、性別、地域)或是瀏覽商品(可能關註喜好、歷史購買記錄)的時候有著不同的含義。所謂的不同的場景,其實就是不同的限界上下文。不同的限界上下文之間,通過上下文映射圖來進行交互。上下文映射圖其實就是一個簡單的框圖,表示限界上下文之間的的映射關系,這張圖就是一個簡單的例子技術分享圖片 U表示上遊被依賴方,D表示下遊依賴方。由於上下遊的限界上下文模型不同,實現時,可以用RPC、Restful、消息機制等集成方式。下遊需要防腐層來將上遊的返回內容翻譯為下遊的領域模型。

關於領域、領域模型、限界上下文的關系:

  • 領域就是問題域,問題空間;
  • 領域模型是一種模型,表達了領域中哪些業務需求以及業務規則必須被滿足;
  • 每一個領域中的問題,都會有一個對應的領域模型去解決;
  • 限界上下文的作用是用來對領域模型進行劃分;
  • 劃分領域就是對問題空間的劃分,通俗的理解,就是將大問題拆分為小問題;
  • 劃分限界上下文就是將一個大的領域模型劃分為多個小的領域模型;
  • 可以把限界上下文看成是一種解決方案空間,所以,限界上下文也可以理解為是對解決方案空間的劃分;
  • 理論上,一個領域可能會對應多個限界上下文;同樣,一個限界上下文可能也會對應多個領域;所以他們之間沒有絕對的關系。主要是他們劃分的依據不同,一個是針對領域(問題空間),一個是針對領域模型(解決方案空間);理想情況,一個領域最好對應一個限界上下文;

關於領域、子領域、核心子域、通用子域,以及共享內核的理解:

  • 一個領域會拆分為多個子領域;
  • 子領域中最核心(最重要)的那個叫核心子域;我們應該講團隊的核心資源用在核心子域上,因為它是產品成敗的關鍵;
  • 除了核心子域外,其他的是支撐子域;
  • 有些支撐子域比較特殊,因為它解決的是一類通用問題,比如賬號和權限;這類子域我們叫做通用子域;通常,通用子域對應的限界上下文,會跨域多個子域;
  • 多個子領域有時會有相交的部分,我們稱作共享內核;體現到代碼上,就是同一份代碼,在兩個領域模型中復用;

實體:實體是有標識的,兩個擁有相同屬性的實體不是相等的,除非它們的標識相等;而不同實體的標識不能相等。實體有生命周期,實體從被創建後可能會被持久化到數據庫,然後某個時候又會被取出來

例如:某人下了兩個相同的訂單,裏面都購買了相同的商品。這兩個訂單就是有標識(訂單號)的兩個實體,雖然內容相同,但它們是兩個不同的實體。實體作為領域模型的主體,需要擁有自己的方法,方法名來自於通用語言。通過這些方法來保證自己始終是一致的狀態,而非被調用者set來set去。例如:people.runTo(x, y),而非people.setX(x);people.setY(y);

值對象:值對象只用於描述或度量一個東西。值對象沒有任何標識,只要兩個值對象的屬性相等,那麽它們就是相等的。值對象是不可變的,如果要改變值對象的內容,那就重新創建一個值對象。值對象沒有生命周期,因為它只是值而已。例如:金額(含數值和貨幣單位),顏色(含rgb值)等

聚合及聚合根:聚合表示一組領域對象(包括實體和值對象),用來表述一個完整的領域概念,而每個聚合都有一個根實體,這個根實體又叫做聚合根。舉個簡單的例子,一個電腦包含硬盤、CPU、內存條等,這一個組合就是一個聚合,而電腦就是這個組合的聚合根。。聚合根是聚合所表述的領域概念的主體,外部對象需要訪問聚合內的實體時,只能通過聚合根進行訪問,而不能直接訪問。關於聚合的劃分學問還是挺大的,需要在實踐中慢慢積累。同一個實體,在不同的聚合中,它可能是聚合根,也可能不是,需要根據實際的業務決定。

聚合根,實體,值對象區別和聯系:技術分享圖片

聚合有以下一些特點:

  1. 每個聚合有一個根和一個邊界,邊界定義了一個聚合內部有哪些實體或值對象,根是聚合內的某個實體;
  2. 聚合內部的對象之間可以相互引用,但是聚合外部如果要訪問聚合內部的對象時,必須通過聚合根開始導航,絕對不能繞過聚合根直接訪問聚合內的對象,也就是說聚合根是外部可以保持 對它的引用的唯一元素;
  3. 聚合內除根以外的其他實體的唯一標識都是本地標識,也就是只要在聚合內部保持唯一即可,因為它們總是從屬於這個聚合的;
  4. 聚合根負責與外部其他對象打交道並維護自己內部的業務規則;
  5. 基於聚合的以上概念,我們可以推論出從數據庫查詢時的單元也是以聚合為一個單元,也就是說我們不能直接查詢聚合內部的某個非根的對象;
  6. 聚合內部的對象可以保持對其他聚合根的引用;
  7. 刪除一個聚合根時必須同時刪除該聚合內的所有相關對象,因為他們都同屬於一個聚合,是一個完整的概念;

如何識別聚合?

這個需要從業務的角度深入分析哪些對象它們的關系是內聚的,即我們會把他們看成是一個整體來考慮的;然後這些對象我們就可以把它們放在一個聚合內。所謂關系是內聚的,是指這些對象之間必須保持一個固定規則,固定規則是指在數據變化時必須保持不變的一致性規則。當我們在修改一個聚合時,我們必須在事務級別確保整個聚合內的所有對象滿足這個固定規則。作為一條建議,聚合盡量不要太大,否則即便能夠做到在事務級別保持聚合的業務規則完整性,也可能會帶來一定的性能問題。有分析報告顯示,通常在大部分領域模型中,有70%的聚合通常只有一個實體,即聚合根,該實體內部沒有包含其他實體,只包含一些值對象;另外30%的聚合中,基本上也只包含兩到三個實體。這意味著大部分的聚合都只是一個實體,該實體同時也是聚合根。

聚合設計的原則

  1. 聚合是用來封裝真正的不變性,而不是簡單的將對象組合在一起;
  2. 聚合應盡量設計的小;
  3. 聚合之間的關聯通過ID,而不是對象引用;
  4. 聚合內強一致性,聚合之間最終一致性;

如何識別聚合根?

如果一個聚合只有一個實體,那麽這個實體就是聚合根;如果有多個實體,那麽我們可以思考聚合內哪個對象有獨立存在的意義並且可以和外部直接進行交互。

聚合根、實體、值對象對象之間如何建立關聯?

聚合根到聚合根:通過ID關聯;聚合根到其內部的實體,直接對象引用;聚合根到值對象,直接對象引用;

實體對其他對象的引用規則:1)能引用其所屬聚合內的聚合根、實體、值對象;2)能引用外部聚合根,但推薦以ID的方式關聯,另外也可以關聯某個外部聚合內的實體,但必須是ID關聯,否則就出現同一個實體的引用被兩個聚合根持有,這是不允許的,一個實體的引用只能被其所屬的聚合根持有;

值對象對其他對象的引用規則:只需確保值對象是只讀的即可,推薦值對象的所有屬性都盡量是值對象;

例子分析:帖子與回復的模型

不 變性分析:帖子和回復之間有不變性規則嗎?似乎我們只知道一點是肯定的,那就是帖子和回復之間的關系,1:N的關系;除了這個之外,我們看不到任何其他的 不變性規則。那麽這個1:N的對象關系是一種不變性規則嗎?不是!首先,一個帖子可以沒有任何回復,帖子也不對它的回復有任何規則約束,它甚至都不知道自 己有多少個回復;再次,發表了一個回復和帖子也沒有任何關系;其次,發表回復對帖子沒有任何改變;從業務場景的角度去分析,我們有發表帖子的場景,有發表 回復的場景。當在發表回復的時候,是以回復為主體的,帖子只是這個回復裏所包含的必要信息,用於說明這個回復是對哪個帖子的回復。這些都說明帖子和回復之 間找不出任何不變性約束的規則;因為帖子和回復都有各自獨立的業務場景的需要,所以可以很容易理解它們都是獨立的聚合根;那也很容易知道該如何建立他們之 間的關聯了,但是我們要盡量減少關聯,所以只保留回復對帖子的關聯即可;帖子沒有任何必要去保存一個回復的ID的列表;那麽你可能會說,當我刪除一個帖子 後,回復應該是沒有存在的意義的呀?不對,不是沒有存在的意義,而是刪除了帖子後導致了回復對帖子的關聯信息的缺失,導致數據不一致。這是因為帖子和回復 之間有一種必然的聯系(1:N),回復一定會有一個對應的帖子;但是回復有其自己的生命周期,不應該隨著帖子的刪除而級聯刪除。這種情況下,如果你刪除了 帖子,就導致回復也成為了一條無效的數據;所以,我們絕對不允許刪除任何聚合根,因為一旦你刪除了聚合根,那就意味著與該聚合根相關的其他任何聚合根都會 有外鍵引用缺失的問題,會導致整個領域模型數據的不一致;所以,永遠都不要刪除聚合根;

領域服務:領域模型主張富領域模式,也就是說把領域邏輯盡量寫在領域實體裏面,也就是常說的“充血模式”,而對於業務邏輯,最好是以服務的形式提供。至於領域邏輯和業務邏輯的界定,這個要根據實際情況來定。如果通用語言裏面出現了名詞,那一般就是實體或值對象;如果裏面出現了動詞,那通常就意味著領域服務。例如:支付,這是一個比較明顯的業務操作。另外,如果有什麽操作會讓實體變得臃腫,也可以使用領域服務來解決。但是不能把所有的東西都堆到領域服務裏,過度使用領域服務會導致貧血對象的產生。良好的領域服務具有以下三個特征:

  • 操作不是實體/值對象的一個自然的部分
  • 接口根據領域模型的其它元素定義
  • 操作無狀態
    還需要註意的是,不要把領域服務和應用服務混起來了。我們在領域服務裏處理業務邏輯,而並不在應用服務裏處理。應用服務是領域模型的直接客戶,負責處理事務、安全等操作。

工廠:工廠是生命周期的開始階段,它可以用來創建復雜的對象或是一整個聚合。復雜對象的創建是領域層的職責,但它並不屬於被創建的對象自身的職責。實體和值對象的工廠不太一樣,因為值對象是不可變的,所以需要工廠一次性創建一個完整的值對象出來。而實體工廠則可以選擇創建之後再補充一些細節。工廠的作用是將創建對象的細節隱藏起來。客戶傳遞給工廠一些簡單的參數,然後工廠可以在內部創建出一個復雜的領域對象然後返回給客戶。領域模型中其他元素都不適合做這個事情,所以需要引入這個新的模式,工廠。工廠在創建一個復雜的領域對象時,通常會知道該滿足什麽業務規則(它知道先怎樣實例化一個對象,然後在對這個對象做哪些初始化操作,這些知識就是創建對象的細節),如果傳遞進來的參數符合創建對象的業務規則,則可以順利創建相應的對象;但是如果由於參數無效等原因不能創建出期望的對象時,應該拋出一個異常,以確保不會創建出一個錯誤的對象。當然我們也並不總是需要通過工廠來創建對象,事實上大部分情況下領域對象的創建都不會太復雜,所以我們只需要簡單的使用構造函數創建對象就可以了。隱藏創建對象的好處是顯而易見的,這樣可以不會讓領域層的業務邏輯泄露到應用層,同時也減輕了應用層的負擔,它只需要簡單的調用領域工廠創建出期望的對象即可

資源庫:資源庫是生命周期的結束,它封裝了基礎設施以提供查詢和持久化聚合的操作。這樣能夠讓我們始終聚焦於模型,而把對象的存儲和訪問都委托給資源庫來完成。以訂單和訂單明細的聚合為例,因為一定是通過訂單這個聚合根來獲取訂單明細,所以可以有訂單的資源庫,但是不能有訂單明細的資源庫。也就是說,只有聚合才擁有資源庫。需要註意的是,資源庫並不是數據庫的封裝,而是領域層與基礎設施之間的橋梁。DDD關心的是領域內的模型,而並非是數據庫的操作。理想的資源庫對客戶(而非開發者)隱藏了內部的工作細節,委托基礎設施層來幹那些臟活,到關系型數據庫、NOSQL、甚至內存裏讀取和存儲數據。

領域驅動設計的經典分層架構:技術分享圖片

用戶界面/展現層:負責向用戶展現信息以及解釋用戶命令。更細的方面來講就是:

  1. 請求應用層以獲取用戶所需要展現的數據;
  2. 發送命令給應用層要求其執行某個用戶命令;

應用層:很薄的一層,定義軟件要完成的所有任務。對外為展現層提供各種應用功能(包括查詢或命令),對內調用領域層(領域對象或領域服務)完成各種業務邏輯,應用層不包含業務邏輯。

領域層:負責表達業務概念,業務狀態信息以及業務規則,領域模型處於這一層,是業務軟件的核心

基礎設施層:本層為其他層提供通用的技術能力;提供了層間的通信;為領域層實現持久化機制;總之,基礎設施層可以通過架構和框架來支持其他層的技術需求;

3.設計DDD的一般步驟:

  1. 根據需求建立一個初步的領域模型,識別出一些明顯的領域概念以及它們的關聯,關聯可以暫時沒有方向但需要有(1:1,1:N,M:N)這些關系;可以用文字精確的沒有歧義的描述出每個領域概念的涵義以及包含的主要信息;
  2. 分析主要的軟件應用程序功能,識別出主要的應用層的類;這樣有助於及早發現哪些是應用層的職責,哪些是領域層的職責;
  3. 進一步分析領域模型,識別出哪些是實體,哪些是值對象,哪些是領域服務;
  4. 分析關聯,通過對業務的更深入分析以及各種軟件設計原則及性能方面的權衡,明確關聯的方向或者去掉一些不需要的關聯;
  5. 找出聚合邊界及聚合根,這是一件很有難度的事情;因為你在分析的過程中往往會碰到很多模棱兩可的難以清晰判斷的選擇問題,所以,需要我們平時一些分析經驗的積累才能找出正確的聚合根;
  6. 為聚合根配備倉儲,一般情況下是為一個聚合分配一個倉儲,此時只要設計好倉儲的接口即可;
  7. 走查場景,確定我們設計的領域模型能夠有效地解決業務需求;
  8. 考慮如何創建領域實體或值對象,是通過工廠還是直接通過構造函數;
  9. 停下來重構模型。尋找模型中覺得有些疑問或者是蹩腳的地方,比如思考一些對象應該通過關聯導航得到還是應該從倉儲獲取?聚合設計的是否正確?考慮模型的性能怎樣,等等;

4.DDD與傳統的 Controller/Service/Dao三層結構模式比較

業務初期,我們的功能大都非常簡單,普通的CRUD就能滿足,此時系統是清晰的,使用三層結構開發方式,對象只是數據的載體,沒有行為。以數據為中心,以數據庫ER設計作驅動。三層架構在這種開發模式下,可以理解為是對數據移動、處理和實現的過程。

但在業務邏輯復雜了,業務邏輯、狀態會散落到在大量方法中,原本的代碼意圖會漸漸不明確,而DDD將數據和行為封裝在一起,並與現實世界中的業務對象相映射。各類具備明確的職責劃分,將領域邏輯分散到領域對象中,解決了代碼高耦合低聚合的問題

DDD試圖解決的是軟件的復雜性問題,如果軟件比較復雜,或者是預期會很復雜,或者是你不知道,那麽都可以開始考慮DDD。否則,由於維系領域模型需要實現大量的封裝和隔離,DDD會帶來較大的成本。但是,DDD並不是一個笨重的開發過程,它能夠和敏捷開發很好地結合起來,另外,DDD也傾向於“測試先行,逐步改進”。

5.DDD與微服務

我們創建微服務時,需要創建一個高內聚、低耦合的微服務。而DDD中的限界上下文則完美匹配微服務要求,可以將該限界上下文理解為一個微服務進程。他們的具體關系如下圖:

技術分享圖片

6.簡單舉例:

需求:舉辦一個比賽,有兩個隊參加,比賽在某個時間開始,只能開始一次,比賽結束後,統計積分
作為用戶,希望看到:參加比賽的隊伍名稱,比賽開始時間,比賽結束時間,比賽結束後的分數。

傳統方式從上面需求中,根據名詞或動詞法則,得到下面類:Match比賽,Team隊伍,Score分數,MatchService,代碼如下:

技術分享圖片 技術分享圖片

這是貧血失血模型,對象只有屬性,沒有自己的行為方法,有的只有setter/getter方法而已。

從需求中,我們會發現有一個聚合詞語: Match(比賽)可以涵括需求的大部分。

那麽“Match比賽”模型無疑是一個實體,是聚合根。它的重要特點是內部有狀態,而且不能向外直接暴露這種狀態;通過聚合根實體和外界進行交互,通過實體“Match比賽”模型,可以創建值對象:兩個隊伍的名稱,對象Team值對象,值對象是不可變的,也可以根據一個比賽名稱開始一個比賽。我們也可以結束一個比賽,這時有值對象分數Score,也具有不可變性。這樣, Match比賽 聚合根實體的代碼如下,相當於將原來MatchService的代碼移到實體類的方法中,再也沒有了服務技術分享圖片

在Match類中,我們只有getter方法,只有把不可變的值對象提供外部訪問getter的方法,如果也去除了getter方法如何?那麽如何產生出給用戶看的視圖數據呢?

我們從讀寫兩個方面去看待模型:

寫模型: 統一語言,顯式的事務邊界,復雜的業務邏輯。
讀模型:專門為讀優化(緩存等),有不同的SQL如NoSQL分析,簡單的類組成。

下一步,如何讓數據滿足這些模型,如果說我們已經撐起來骨架,那麽數據是領域模型的血液,如何將血液輸送到模型中呢? 數據一般會保存在數據庫或各種NoSQL中。所以可以使用事件。那麽我們在實體模型Match中的方法中增加事件代碼如下:

技術分享圖片

對於聚合根中的字段,我們只需要那些能夠改變業務方法行為的字段,其他數據都被歸納入事件對象中,在Match類中,是否結束這種狀態對業務行為影響大,因此,代碼改變最後如下:

技術分享圖片

本文只是粗淺介紹了下DDD,demo來源於http://www.jdon.com/44815

文章大部分摘自湯雪華的連載博客共23篇,涵蓋了DDD的方方面面,如想深入研究,請參考:http://www.cnblogs.com/netfocus/category/361987.html

其余參考如下:https://tech.meituan.com/DDD%20in%20practice.html

https://www.jianshu.com/p/b6ec06d6b594

https://kb.cnblogs.com/page/576236/

領域驅動設計