領域驅動設計理解&總結

来源:https://www.cnblogs.com/zhangxiaoguang/archive/2018/12/12/ddd.html
-Advertisement-
Play Games

領域驅動設計理解&總結 這篇文章主要是通讀《實現領域驅動設計》之後自己的理解和總結(同時也參照一些博文的分析來加深自己的理解); 有些疑問是自定義內容,雖然有自己的理解,但依然感覺較為抽象,後續會通過實踐來理解其中的精妙之處。 ...


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

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

領域驅動設計指引

  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. 領域驅動設計實踐


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • tofixed方法 四捨五入 toFixed() 方法可把 Number 四捨五入為指定小數位數的數字。例如將數據Num保留2位小數,則表示為:toFixed(Num);但是其四捨五入的規則與數學中的規則不同,使用的是銀行家舍入規則,銀行家舍入:所謂銀行家舍入法,其實質是一種四舍六入五取偶(又稱四舍 ...
  • 示例html代碼: 示例html代碼: 示例html代碼: 示例html代碼: 示例html代碼: <div id="test"> <span style="color:red">test1</span> test2 </div> 獲得id為test的DOM對象,下麵就不一一獲取了。 var tes ...
  • 面向對象的語言有一個標誌,那就是它們都有類的概念,而通過類可以創建任意多個具有相同屬性和方法的對象。 理解對象 創建自定義對象的最簡單的方法就是創建一個Object的實例,然後再為它添加屬性和方法。例如: 同樣上面的例子可以通過對象字面量語法寫成如下: 屬性類型 ECMAScript中有兩種屬性:數 ...
  • Steps步驟條組件源碼: steps.vue step.vue ...
  • 1、Node.js簡介 簡單的說 Node.js 就是運行在服務端的 JavaScript。Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型,使其輕量又高效。Node.js 的包管理器 npm,是全球 ...
  • 如何學好面向對象? 面向對象雖然只有三個特性,封裝、繼承、多態,但是真正面向對象卻是說的容易做起來困難。但是,還是有一定的規則可尋的, 要學好面向對象,必須掌握設計模式 。 什麼是設計模式? 設計模式(Design pattern):是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結 ...
  • 本文介紹了Java驗證的幾種機制,包括JPA驗證,Bean驗證,實體監聽器和事務監聽器。通過介紹希望可以在Java項目整體的驗證方面提供一些參考。 ...
  • 解釋器模式是一種不很常用的模式,但是比如正則表達式就是一種解釋器模式的思維,所以儘管實際編碼中不常用,理解解釋器模式的含義很重要,本文對解釋器模式進行了簡單的介紹,並且給出了Java代碼示例,介紹瞭解釋器模式的意圖結構。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...