領域驅動設計(DDD)-基礎思想
轉載自:領域驅動設計(DDD)-基礎思想
一、序言
領域驅動設計是一種解決業務複雜性的設計思想,不是一種標準規則的解決方法。在領域驅動設計理念上,各路大俠的觀點也是各有不同,能力有限、歡迎留言討論。
二、領域驅動設計
DDD是什麼
wiki釋義:
領域驅動設計(英語:Domain-driven design,縮寫 DDD)是一種通過將實現連線到持續進化的模型[1]來滿足複雜需求的軟體開發方法。領域驅動設計的前提是:
- 把專案的主要重點放在核心領域(core domain)和域邏輯
- 把複雜的設計放在有界域(bounded context)的模型上
- 發起一個創造性的合作之間的技術和域界專家以迭代地完善的概念模式,解決特定領域的問題
領域驅動設計是一種由域模型(牆裂推薦@阿白 的域模型系列)來驅動著系統設計的思想,不是通過儲存資料詞典(DB表字段、ES Mapper欄位等等)來驅動系統設計。領域模型是對業務模型的抽象,DDD是把業務模型翻譯成系統架構設計的一種方式。
術概念
DDD中的模型
Model與傳統的POJO(DTO、DO、DAO)類等對比,都是一個類中有屬性、屬性有Get/Set方法,並且做傳輸物件。
Model與傳統MVC三層架構層的業務邏輯層中的Service對比,都是處理業務行為(Action)層。
模型(Model)承載著業務的屬性和具體的行為,是業務表達的方式、是DDD的核心。是一個類中有屬性、屬性有Get/Set方法,並且業務的行為(Action)操作也是在模型類中(充血模型)即做業務邏輯處理,又做資料傳輸物件,模型分為Entity、Value Object、Service這三種類型。
- Entity (實體)
- 有特定的標識,標識著這個Model在系統中全域性唯一
- 內部值可以是變化的,可能存在生命週期 (比如訂單物件,狀態值是連續變化的)
- 有狀態的Value Object
- Value Object (值物件)
- 內部值是不變的,不存在生命週期 (比如地址物件不存在生命週期)
- 無狀態物件
- Service (服務)
- 無狀態物件
- 當一個屬性或行為放在Entity、Value Object中模稜兩可或不合適的時候就需要以Service的形式來呈現
三種模型的複雜度是不一樣的,在領域建模選Model模稜兩可時,優先選擇簡單模型原則。模型複雜度順序 Service > Entity > ValueObject
DDD模型的生命週期
- Factory (工廠)
用來建立Model,以及幫助Repository (資料來源)注入到Model中
- Aggreagte (聚合根)
封裝Model,一個Mode中l可能包含其他Model(類似一個物件中包含其他物件的引用,實際概念更為複雜些)
- Repository (資料來源)
資料來源的訪問閘道器層、通過Repository來對接不同的資料來源
DDD模型的邊界
- 限界上下文,領域邊界上下文
- 域的拆分
- 按業務抽象進行劃分
- 一個業務拆分成幾個獨立的域,每個域又可細拆成不同子域
- 防腐
- 一個域在訪問其他域的模型時,把獲取到的模型做層轉換對映到自己域的模型中(不直接使用別的域模型作為自己域模型中的一部分)
- 防止源域模型發生變更,依賴源域模型的呼叫方,在需要源域模型新功能時,必須要全域性依賴修改,才在能相容
- 防止域上下文不一致產生的衝突
其他
一個團隊,一種語言
一條業務線由研發、產品、業務共同協作和維護,大家只是在不同維度做同一件事情。領域驅動設計的實現方式不僅僅是程式碼,也可以是PRD、MRD、業務模型圖,選擇一種大家都能看懂的方式(領域模型術語),統一團隊語言,從上層業務到底層實現都是同一個領域模型,減少資訊傳遞經過翻譯造成理解不一致。
DDD設計的特點
根據業務模型設計系統
不是通過資料庫等資料來源驅動設計,是根據業務語義抽象梳理設計成領域模型
資料模型統一
通過真實業務背景,梳理出業務域模型自然會形成出參、入參、中間臨時屬性收口統一為域模型
業務模型與資料來源無關
- 資料來源資料結構無論怎麼變、資料來源無論怎麼換,領域模型統一無感知,無須變更。
- 一個域模型底層對應的資料來源可以是1個或n個不同型別資料來源
- 系統升級底層資料來源結構改造時,變更對業務層是透明,域模型可無縫對接,可達到開著飛機換引擎的效果
業務屬性欄位命名統一、引用唯一
在現在MVC模式開發中,入參model、資料傳輸model、資料來源model 同一個業務屬性含義可能有多種不同的命名,引用情況很難直接排除,當豐富某個業務欄位值時,很難直接判斷對原有業務的影響範圍
業務行為Action收口
在原有開發模式下,一個Model類是一個POJO、DTO、DO,僅做資料傳輸,沒有任何業務相關Action,屬於典型的貧血模型。在DDD中一個Model就表述一個業務的域(可能是子域),這個Model不僅有屬性,還有業務行為Action,並且這個域的所有操作都在這個Model中,這個Model不僅是資料傳輸的作用也是一個具體的Service,是屬於充血模型。開發人員可以通過關注這個域模型就可以cover負責領域的全部,更不會出現大量的複製-貼上重複程式碼。
業務操作高內聚、低耦合
所有這個域的操作都內聚在這個Model中,不會存在同一個相同業務行為在多個Service中存在現象。很多時候一個業務行為功能變更,在原有開發模式下需要把所有的service中有這個業務行為的地方都要變更(貼上複製程式碼更為嚴重)
系統更能直觀體現業務邏輯
產品是一直在演進的,PRD、技術方案都很難準確的表明現在產品的真實邏輯,很多時候大家都會遇見這種現象,產品經理不確定當前某個業務點的準確邏輯,需要開發閱讀程式碼翻譯給產品經理業務邏輯。
鐵打的程式碼、流水的產研,產品經理、開發流動性都很大,新人僅看文件cover整個系統,很難做到。每個業務準確的細節點,還是要看系統程式碼實際的邏輯規則。
領域驅動架構
領域驅動設計沒有特定的架構風格,它的核心是域模型驅動業務的思想,常見的領域驅動設計架構有經典的三層架構、REST架構、事件驅動架構、CQRS架構、六邊形架構等。
領域驅動三層架構
API層
API層是作為對外打包、前端介面呼叫使用。Domian層是整個域模型,不能直接把它打包成maven給別人使用,也不能直接把它作為介面給前端使用,有些需要API層作為進行轉換後呼叫Domain,對呼叫Domain返回的資料進行包裝篩選後再返回出去。
Domain層
系統的核心層,所有具體的業務邏輯處理、事件處理等都在這層域模型中處理
Repository 層
資料來源代理層,Repository 層類似一個閘道器代理,它本身沒有資料,資料都是通過它的代理來被Domain層訪問,被代理的資料來源不僅僅可以是DB、ES還可以是HTTP、RPC任何與Domain層進行資料互動的都叫Repository
領域驅動設計優勢和劣勢
DDD不是銀彈,它只是複雜性業務的一種解決方式。DDD解決了系統設計的‘複雜性’,DDD設計思想本身又存在複雜性。
優勢
系統演進更方便
隨著業務的變化、系統設計也要演進升級。好的架構設計一定演化來的,不是一開始就設計出來的,但系統演進過程中的成本,一定是最開始的設計決定的。一個健康公司的成長,業務橫向、縱向會發展的會越來越複雜,支援業務的系統也一定會越來越複雜。在領域驅動設計中,域模型對應的是業務模型,是系統架構的核心,通過域模型來驅動與外界的互動。
業務複雜性變化的演進
域模型可能是簡單新增屬性或action就能支撐整體的業務發展。企業訂餐的業務系統要同時有使用者端、運營端、企業端、商戶端的資料展示和操作,當業務演進出一個新功能時,這四端系統可能都要同時改造支撐。在領域驅動中,系統的域模型是同一套,只需在領域層進行改造,即可同時支撐四端。
業務資料量變化的演進
公司業務資料量的變化後,現有的架構往往很難支援業務的發展,一定會進行新的技術選型支援業務。在DDD中,域模型為核心,在核心外的一層是代理層,通過這層代理來抽象透明化掉業務模型對系統底層設計的感知。比如原本資料量很小,一個簡單的搜尋直接使用MySQL like 模糊查詢即可滿足,在資料量巨大這種方式無法滿足的時候,需要使用ES這種專業的搜尋技術來實現,這時候僅需要在資料來源層把原本指向資料來源MySQL改成ES即可,業務程式碼全程透明無感知,可以達到給正在飛行的飛機換引擎的效果。
更方便測試
對於測試(包含開發自測)來說,流程跑不通是痛苦的,由於IO造成阻塞而非系統邏輯是更痛苦。測試的時候最喜歡的純函式測試,不依賴任何IO(不包含機器記憶體層面),DDD設計思想是天然的在程式碼上把純函式和普通函式區分開,Repository層是非純函式,在Repository層Mock掉,整體系統就成了純函式系統,對測試在Mock資料、切換資料來源是非常方便和友好的。
劣勢
系統改造成DDD複雜
我們常用的架構基本都是MVC三層架構方式,在常用的MVC三層架構中基本所有的業務邏輯都在service層中,並且是按service功能屬性設計的Service層,現在要進行DDD思想開發,需要打破原有的設計,有些嚴重的還必需要進行重構設計。
團隊開發熟悉DDD思想困難
改變自己比較困難,對別人產生影響更加困難。一個開發團隊如果之前對DDD都沒有了解,要推進和對團隊產生影響是一個艱難的過程。
最後
DDD不僅是統一語言、以業務驅動系統設計,在熟悉新業務和系統重構時,領域驅動設計思想更能很好快速梳理業務。如下圖領域驅動設計是以領域(業務模型)為核心,通過資料代理層(Repository)來與其他系統互動,來驅動整個系統架構設計。
三、參考引用
【1】域驅動開發
【3】如何進行域劃分設計