領域驅動設計理解&總結


領域驅動設計理解&總結 這篇文章主要是通讀《實現領域驅動設計》之后自己的理解和總結(同時也參照一些博文的分析來加深自己的理解);

有些疑問是自定義內容,雖然有自己的理解,但依然感覺較為抽象,后續會通過實踐來理解其中的精妙之處。

領域驅動設計指引

  1. 領域驅動設計 作為一種軟件開發方法,提供了戰略上(思考方式) 和 戰術上(落地方式) 的建模工具來幫助我們 設計高質量的軟件模型;

  2. 領域驅動設計 不是關於技術的,而是關於討論、聆聽、理解、發現業務價值 的,目的是將 知識 集中起來,形成 通用語言(Ubiquitous Language);

  3. 在 領域驅動設計 中,技術也重要,但更重要的是要掌握 領域建模 中更高層次的概念;

領域建模

在領域中構建模型,什么是 領域模型?

  • 關於 某個特定業務領域的軟件模型。通常,領域模型通過 對象模型 來實現,這些對象包含了數據和行為,並且表達了准確的業務含義。

為什么要使用DDD

Vaughn Vernon(沃恩.弗農) 在他的書里(《實現領域驅動設計》)闡述了很多,總結3點:

  1. 領域專家、開發者、業務人員等都掌握同樣的軟件知識,大家都使用相同的語言進行交流,每個人互相理解彼此在說什么;

  2. 設計就是代碼,代碼就是設計,軟件能夠表達大家所理解的意思;

  3. DDD持續關注業務,會產生一些業務價值

    1. 獲得一個有用的 領域模型;

    2. 業務得到更准確的 定義和理解;

    3. 領域專家可以為領域設計做出貢獻;

    4. 更好的用戶體驗(軟件本身容易上手,減少培訓,提高效率)

    5. 清晰的模型邊界

    6. 良好的企業架構

    7. 敏捷、迭代式和持續建模

    8. 使用 戰略和戰術工具

DDD推進過程中存在的挑戰

  1. 創建通用語言(所有人達成一致的某個業務術語的統一認知)消耗額外的時間和精力

  2. 持續的將領域專家引入項目

  3. 改變開發者 對領域的思考方式

如何使用DDD

DDD 提供了戰略上 和 戰術上 的建模工具來幫助我們 設計高質量的軟件模型,戰略設計 側重於高層次、宏觀上去划分和集成限界上下文,而 戰術設計 則關注更具體使用建模工具來細化上下文。

戰略上

  1. 限界上下文(Bounded Context) 為團隊創建一個建模邊界;

  2. 成員在邊界內部為特定的 業務領域 創建 解決方案;

  3. 單個限界上下文中團隊成員共享一套 通用語言(Ubiquitous Language);

  4. 不同團隊各自負責一個限界上下文,可以使用 上下文映射圖 對限界上下文進行 界分和集成;

戰術上

  1. 實體(Entity)

  2. 值對象(Value Object)

  3. 聚合(Aggregate)

  4. 領域服務(Domain Service)

  5. 領域事件(Domain Event)

  6. 資源庫(Repository)

下邊我針對 戰略和戰術 兩個方面進行講解

DDD之戰略

領域

廣義上講:領域 是一個組織所做的事情以及其中所包含的一切(為某個組織開發軟件時,你面對的就是這個組織的 領域)。

軟件開發中:領域 既可以表示 整個業務系統,也可以表示系統中的 核心域 或者 支撐子域。

在DDD中:領域 被划分為若干 子域,領域模型 在 限界上下文 中完成開發。

領域 包括下邊三種 子域:

  1. 核心域(業務成功的主要促成因素,是企業的核心競爭力,應該給予最高的優先級、最資深的領域專家和最優秀的開發團隊,實施DDD的過程中主要關注於 核心域);

  2. 支撐子域(不是核心,對應業務的 某些重要方面,有時我們會創建或者購買某個支撐子域);

  3. 通用子域(不是核心,但被整個業務系統所使用);

現實世界中的領域

現實世界中的領域包括 問題空間(Problem Space)和 解決方案空間(Solution Space):

  • 問題空間:是核心域和其他子域的組合,思考的是 業務面臨的挑戰

  • 解決方案空間:一組特定的 軟件模型,包括一個或多個限界上下文,思考的是如何實現軟件(限界上下文 即是一個 特定的解決方案,通過軟件的方式實現解決方案)以 解決這些業務挑戰

限界上下文

  • 一個 顯式的邊界(主要是一個語義上的邊界),領域模型便存在於這個邊界之內;每一個模型概念(包括它的屬性和操作)在邊界之內都具有特殊的含義;

  • 一個 給定的業務領域 會包含多個限界上下文,想與一個限界上下文溝通,則需要通過顯示邊界進行通信;系統通過確定的限界上下文來進行解耦,而每一個上下文內部緊密組織,職責明確,具有較高的內聚性;

  • 一個很形象的隱喻:細胞質所以能夠存在,是因為細胞膜限定了什么在細胞內,什么在細胞外,並且確定了什么物質可以通過細胞膜(引用);

與技術組件保持一致

將限界上下文想象成技術組件是可以的,但是技術組件並 不能來定義(是說不能定義概念?) 限界上下文,有幾種做法:

  1. 在使用 IntelliJ IDEA 時,一個 限界上下文 通常就是一個工程項目;

  2. 在使用Java時,頂層包名通常表示 限界上下文中頂層模塊 的名字;

  3. 一個團隊,一個限界上下文(即便項目按分層架構模塊划分,團隊依然應該只工作在一個限界上下文中);

上下文映射圖

確定了單個限界上下文之后,有時還需要確定多個限界上下文之間的關系,這時就需要上下文映射圖

一個項目的 上下文映射圖 可以用兩種方式來表示:

  1. 畫一個簡單的框圖來表示 兩個或多個 限界上下文 之間的 映射關系(該框圖表示了不同的限界上下文在 解決方案空間 中是如何通過集成相互關聯的);

  2. 通過 限界上下文 集成的源代碼實現來表示;

限界上下文界分和集成

康威定律 告訴我們,系統結構 應盡量與 組織結構 保持一致

  • 這里認為團隊結構(無論是內部組織還是團隊間組織)就是 組織結構,限界上下文 就是 系統結構;

  • 因此,團隊結構 應該和 限界上下文 保持一致。

梳理清楚上下文之間的關系,從 團隊內部 的關系來看,有如下好處:

  1. 任務更好拆分(一個開發人員可以全身心的投入到相關的一個單獨的上下文中);

  2. 溝通更加順暢(一個上下文可以明確自己對其他上下文的依賴關系,從而使得團隊內開發直接更好的對接);

從 團隊間 的關系來看,明確的上下文關系能夠帶來如下幫助:

  1. 每個團隊在它的限界上下文中能夠更加明確自己領域內的概念(因為限界上下文是領域的 解決方案空間);

  2. 對於限界上下文之間發生交互,團隊與限界上下文的一致性,能夠保證我們明確對接的團隊和依賴的上下游;

限界上下文之間的映射關系

  • 合作關系(Partnership):兩個限界上下文建立起來的一種 緊密合作關系,要么一起成功,要么一起失敗;

  • 共享內核(Shared Kernel):兩個限界上下文緊密依賴共享的 部分模型和代碼;

  • 客戶方-供應方開發(Customer-Supplier Development):兩個限界上下文有計划的 產生相互依賴(當兩個團隊處於上下游關系時,下游團隊開發會受到上游開發的影響,上游團隊計划應該估計下游團隊的需求);

  • 遵奉者(Conformist):下游限界上下文只能 盲目依賴 上游限界上下文的現象;

  • 防腐層(Anticorruption Layer):一個限界上下文通過轉換和翻譯與其他的限界上下文進行交互;

  • 開放主機服務(Open Host Service):定義一種協議,讓其他限界上下文通過該協議對本限界上下文進行訪問;

  • 發布語言(Published Language):兩個限界上下文之間翻譯模型所需要的公用語言,通常與開放主機服務一起使用;

  • 另謀他路(Separate Way):兩個限界上下文之間不存在任何關系,尋找另外更簡單、更專業的方法來解決問題;

  • 大泥球(Big Ball of Mud):混雜在一起的、邊界非常模糊的限界上下文關系;

領域/上下文划分的原則

在划分的過程中,經常糾結的一個問題是:這個模型(概念或數據)看起來放這個領域合適,放另一個也合適,如何抉擇 呢?

  1. 依據該模型與邊界內其他模型或角色 關系的緊密程度(比如,是否當該模型變化時,其他模型也需要進行變化;該數據是否通常由當前上下文中的角色在當前活動范圍內使用);

  2. 服務邊界內的 業務能力職責應單一,不是完成同一業務能力的模型不放在同一個上下文中;

  3. 划分的子域和服務需滿足 正交原則(模塊的獨立性,領域名字代表的自然語言上下文保持互相獨立);

  4. 組織中 業務部分的划分 也是一種參考(組織架構,一個業務部門的存在往往有其獨特的業務價值);

簡單打個比方,同一個領域上下文中的模型要保持 近親關系,五福以內,同一血統(業務)。

DDD之戰術

實體

當一個對象由其 唯一的身份標志 區分、具有可變的特性,這種對象即為實體。

  • 實體屬性的驗證可以放在實體內部進行

值對象

將領域概念建模成 值對象 的時候,應該將通用語言考慮在內,這是前提。(為什么將通用語言考慮在內?值對象 是領域里的一個概念,大家要統一認知,用通用語言作為標准,可以達到共同理解的目的)

構建值對象,要了解 值對象 以下的特點:

  1. 它度量或者描述了 領域中的一件東西;

  2. 它可以作為 不變量;

  3. 它將不同的相關的屬性組合成一個 概念整體;

  4. 當度量和概念改變時,可以用另一個值對象予以 替換;

  5. 它可以和其他值對象進行相等性 比較;

  6. 它不會對協作對象造成任何副作用;

在實踐中,需要保證值對象創建后就不能被修改,即不允許外部再修改其屬性(如:在訂單上下文中如果你只關注下單時商品信息快照,那么將商品對象視為值對象是很好的選擇)

聚合

  • 聚合(Aggregate)是一組相關對象的集合,作為一個整體被外界訪問,它由 實體 和 值對象 在 一致性邊界之內 組成,聚合根(Aggregate Root)是這個聚合的根節點。

聚合的設計原則

  1. 在設計聚合時,我們需要慎重的考慮 一致性

    1. 關注聚合的 一致性邊界,在一致性邊界之內建模真正的 不變條件(不變條件 指的是業務規則)

      1. 同一個事務之內不能修改多個 聚合實例

    2. 在邊界之外使用 最終一致性

  2. 設計 小聚合;(“小” 的極端意思是指 一個聚合只擁有全局標識和單個屬性;這種做法不推薦)

  3. 通過唯一標識來引用其他聚合或實體:當存在對象之間的關聯時,建議引用其唯一標識而非引用其整體對象(如果是外部上下文中的實體,引用其唯一標識或將需要的屬性構造值對象)

注:如果聚合創建復雜,推薦使用 工廠方法 來屏蔽內部復雜的創建邏輯

領域服務

  • 當領域中的某個操作過程或轉換過程不是實體或者值對象的職責時,將該操作放在一個單獨的接口中,即 領域服務。

領域服務的特點

  1. 領域服務 和通用語言一致,表示 無狀態 的操作,它用於實現特定於某個領域的任務;

  2. 某個操作不適合放在實體(聚合)與值對象上時,適合 領域服務;

    1. 執行一個顯著的業務操作過程;

    2. 對領域對象進行轉換;

    3. 以多個領域對象作為輸入進行計算,結果產生一個值對象;

  3. 領域服務 是用來處理業務邏輯的(我們不能將業務邏輯放到應用層,即使非常簡單,它依然是業務邏輯)

領域事件

  • 對 領域中 所發生的事件(領域專家所關心的發生在領域中的一些事件)進行建模,即 領域事件(領域模型 的組成部分)

領域事件的特點

  1. 領域事件 用來捕獲領域中發生的一些事情,開始使用領域事件時,要 對不同的事件進行定義;

  2. “當...時,請通知我” 等等場景

領域事件發布方法

  1. 限界上下文內,觀察者模式 是一種簡單高效的發布領域事件的方法;

  2. 限界上下文外,利用 消息機制 將本地限界上下文產生的事件發送到 遠程限界上下文 中(我們要保證 所有限界上下文 的最終一致性);

資源庫

  • 對領域的存儲和訪問進行統一管理的對象,即 資源庫(Repository);

資源庫的特點

  1. 通常我們將 聚合實例 存放在資源庫中,之后再通過資源庫獲取相同的實例;

  2. 通常來說,聚合類型 和 資源庫之間存在着 一對一的關系;

    1. 當兩個或多個聚合位於同一個對象層級中時,他們可以共享同一個資源庫;

注:資源庫和 DAO是不同的,一個DAO主要從數據庫表的角度來看待問題,並且提供 CRUD 操作

DDD之架構

極簡化架構設計主要從下邊三個角度出發:

  • 業務架構:根據業務需求設計業務模塊及交互關系;

  • 系統架構:根據業務需求設計系統和子系統的模塊;

  • 技術架構:根據業務需求決定采用的技術及框架;

DDD的核心訴求 就是能夠讓 業務架構 和 系統架構 形成綁定關系,從而當我們去響應 業務變化 調整業務架構時,系統架構的改變是隨之自發的

這個 業務變化 的結果有兩個:

  1. 業務架構 的梳理和 系統架構 的梳理是同步漸進的,其結果是划分出的 業務上下文 和 系統模塊結構 是綁定的;

  2. 技術架構 是解耦的,可以根據划分出來的業務上下文的系統架構選擇最合適的實現技術;

架構類型

分層架構
  • 所有架構的始祖,支持N層架構系統,將一個應用程序或者系統分為不同的層次

DDD使用的傳統分層架構:

分層架構原則:每層只能與其下方的層發生耦合(分層架構 分為 嚴格分層架構 和 松散分層架構)。

嚴格分層架構:每層只能和直接位於其下方的層發生耦合。

松散分層架構:任意上方層與任意下方層發生耦合。

六邊形架構

六邊形結構(端口與適配器架構、onion架構):一種具有對稱性特征的架構風格。

為什么是6邊形?不是4邊形或8邊形?

六邊形架構視角:架構中存在兩個區域,“外部區域”和“內部區域”,外部區域 供給客戶提交輸入,內部區域 獲取持久化數據、對數據進行存儲或轉發。

面向服務架構

服務設計原則

  1. 服務契約:通過契約文檔,服務闡述自身的目的與功能;

  2. 松耦合:服務將依賴關系最小化;

  3. 服務抽象:服務只發布契約,隱藏內部邏輯;

  4. 服務重用性:一種服務可被其他所有服務重用;

  5. 服務自治性:服務自行控制環境與資源以保持獨立性;

  6. 服務無狀態性:服務負責消費方的狀態管理;

  7. 服務可發現性:客戶可通過服務元數據來查找服務和理解服務;

  8. 服務組合性:一種服務可用由其他服務組合而成,不用管其他服務的大小和復雜性如何;

參考資料

  1. 《實現領域驅動設計》

  2. 領域模型的應用 相關文章

  3. 領域驅動設計實踐


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM