快速理解DDD領域驅動設計架構思想-基礎篇

2023-09-06 12:02:17

1 前言

本文與大家一起學習並介紹領域驅動設計(Domain Drive Design) 簡稱DDD,以及為什麼我們需要領域驅動設計,它有哪些優缺點,儘量用一些通俗易懂文字來描述講解領域驅動設計,本篇並不會從深層大論述講解落地實現,這些大家可以在瞭解入門後再去深層次學習探討或在後續進階和高階篇瞭解,希望通過本文介紹,可以讓大家快速瞭解DDD並有一個基礎的認知,DDD本身就是理論的集合,很難在不積累理論情況下來有效的實施DDD,僅僅看一些程式碼案例後就開搞,最終出來東西也是東施效顰,莫要好高騖遠。 最後期望大家在工作中能多思考,如你所負責專案如果用DDD如何設計、以及會面臨哪些挑戰。

學習瞭解DDD之前,期望大家可在溫顧下以往我們所瞭解掌握一些知識,努力讓自己所學所掌握的內容沉澱下來,推薦閱讀系列。

  • Head First 設計模式:基礎物件導向概念和重要的設計模式;
  • UML物件導向建模基礎:從需求到分析,從分析到設計,從設計到編碼,UML都有用武之地
  • 實現領域驅動設計:很厚,更加務實,推薦閱讀
  • 領域驅動設計:張逸-DDD開山之作,挺玄幻的,多讀幾遍受益匪淺;

2 定義與概念

領域驅動設計(DDD)提出是從系統的分析到軟體建模的一套方法論。將業務概念和業務規則轉換成軟體系統中的概念和規則,從而降低或隱藏業務複雜性,使系統具有更好的擴充套件性,以應對複雜多變的現實業務問題。總結它是一套完整而系統的設計方法、是一種設計思維、一種方法論,並不是"系統架構",一種架構設計原則、思維。

2.1、為什麼要使用"領域驅動設計",或者說其用途,應用場景式什麼?

  1. 善於處理高複雜業務的產品研發、可幫助我們提煉穩定的產品核心(領域模型中稱為核心域);

  2. 通過建模可提高建模高內聚、降低模型間的耦合度,提高系統的可延伸性與穩定性;

  3. 強調團隊與領域專家的合作溝通,有助於建立一個溝通良好的團隊組織;

  4. 統一設計思想與設計規範,有助於提高團隊成員的架構設計能力和物件導向設計能力;

  5. 現有的微服務建構都是遵循領域驅動設計的架構原則;

  6. 如果你負責的軟體系統並不複雜,那麼,你確實不需要學習領域驅動設計!

2.2、領域驅動設計跟時下流行的架構思維最大的區別是什麼?

領域驅動設計的思維是:物件+行為+服務,所有的設計圍圍繞著物件、行為、服務展開;

時下流行架構設計思維是:基於MVC分層架構進行縱向擴充套件,分業務模組進行產品橫向擴充套件;

2.2.1. 傳統的方案

三層應用架構:資料-應用(業務邏輯層)-展現,通常是以資料位為起點進行資料庫分析設計。

  1. 服務層過重,資料模型失血,沒東西;

  2. 麵條式程式設計或者面向資料庫程式設計,服務層圍繞資料庫作業完成業務邏輯,經常一條線擼到底;

  3. 程式碼一整塊一整塊的過重,很難擴充套件複用;

  4. 資料庫模型只是資料庫對映,沒有相關的行為支撐,行為都被上一層Service給完成等了,因此是失血 的領域模型;

2.2.2. 領域驅動方案

架構四層在DDD分層結構中將三層中業務邏輯拆解為應用層和領域層,核心業務邏輯表現下沉到領域層去實現,以業務領域模型為核心建模(物件導向建模),更能體現對現實世界的抽象,其優點如下

  1. 輕服務層+充血的領域模型;
  2. 領域模型封裝和實現各自應有的行為,可以認為是一個高內聚、低耦合的元件;
  3. 由於模型集資料與行為於一身,是一種自解釋的物件,程式碼複用性高,業務邏輯清晰明確;
  • 使用者介面層:主要職責是通過使用者介面向用戶顯示資料資訊,同時解釋使用者的命令,並把使用者的請求傳送到應用層。
  • 應用層:通過呼叫基礎設定和領域層完成資料資源操作及業務流程編排,相當於BS層;
  • 領域層:將業務邏輯高度內聚到領域層,所以領域層是整個系統的核心,它只與實際業務相關,不關心任何技術細節,儘可能做到與持久化無關;
  • 基礎設施層:包含了任何型別的框架、資料庫存取程式碼或者公共的方法等,純技術的一層;

2.3、如何學習領域驅動設計

沒有誰能夠做到領域驅動設計的一蹴而就,所謂"理論聯絡實際",在剛開始接觸或學習設計領域驅動時,總會有一種訴求希望能給出公式般的設計準則或規範,似乎軟體設計就像拼積木一般,只要遵循圖示給出的拼搭過程,不經思考就能拼出期待的模型,這似乎不切實際的幻想,要掌握領域驅動設計,首先要了解掌握一些概念以知識理論,在此基礎之上思考這些概念背後蘊含的原理,設計原則,思考限界上下文(Bounded Context)邊界的劃分,實際還是圍繞"高內聚、低耦合"原則的體現,只是我們考慮什麼內容才是高內聚,如何抽象才能做到低耦合,在分層架構中,各層之間該如何共同作業?出現了依賴如何解耦,仍然需要從重用與變化的角度去思考設計決策。

3 領域驅動設計

領域驅動設計強調以"領域"為核心驅動力,通過模型驅動設計來保障領域模型與程式設計的一致,領域模型不應該包含任何技術實現因素,模型中的物件真實的表達了領域概念,卻不受技術實現的約束,領域模型本身和技術無關,領域驅動從設計上劃分為戰略設計和戰術設計。

  • 一個領域是由一個或多個模型組成;
  • 從定義上講模型是領域的抽象;
  • 從理解上將模型可以認為是一個高內聚、低耦合的元件、模組,也可以稱為一個子領域;
  • 模型重在建模過程,建模過程會抽象出一系列領域物件和領域服務;
  • 在DDD中,定義一系列的標準"領域元素"用於領域建模;如戰術設計元模型

3.1. 戰略設計

強調業務戰略上的重點,如何按重要性分配工作,以及如何進行最佳,遵循量大原則:

  1. 必須指導設計決策,以便減少各個部分之間的相互依賴,在使用設計意圖更為清晰的同時而又不失去關鍵的互操作性和系統性;

  2. 必須把模型的重點放在捕獲系統概念核心,也就是系統的"遠景"上。

3.1.1 子域劃分

整個業務領域的一部分,關注與宏觀業務,通過對大領域進行劃小在業務間劃分出概念上分界線,便於在系統籌劃階段決策如何分配資源(人、錢)。

  1. 核心子域:領域中最有價值和最核心的部分,產品成敗的關鍵,核心競爭力,在DDD開發中,主要關注核 心域,給予最高優先順序;
  2. 支撐子域:專案中對核心子域起著支撐作用的相關功能,專注於業務的某個反面;
  3. 通用子域:與專案意圖無關的內聚子領域,解決一些通用問題,任何專有的業務都不應該放在通用子域;

3.1.2. 戰略建模

關注點在於系統物理劃分,根據你對領域的分割結果及公司或部門的組織結構決策如何劃分子系統,比如分出幾個,系統間如何互動,該層面往往暫不會涉及夠多技術細節。

  1. 限界上下文(Bounded Context):通俗講指系統中模組,微服務架構中的子服務、單體中"包(Java)"或名稱空間(C#),對系統的一個物理劃分,限定了領域模型的邊界,在更深層次介紹深挖,這東西得分成兩個詞:限界、上下文,可以理解成系統設計之初,你需要畫一個圈設定一個範圍,保證領域模型限制在這個圈內不可串場,這個‘圈’即為限界上下文。
  2. 通用語言:用於統一領域專家、產品、研發、測試大家在使用的語言,避免出現需求理解不一致,設計與需求不一致,溝通不順暢等問題,簡單來說大家在一起聊某個東西的時候都能明白彼此所致的是什麼,場景不同,同一個詞就會有著不同含義。
  3. 上下文對映圖:用圖的方式,表達出限界上下文之間關聯,後續會單獨在詳細介紹上下文對映圖,如 合作關係、防腐層、大泥球等;

3.2. 戰術設計

依賴於領域模型和通用預言,通過技術模式將領域模型和通用預言中的概念對映到程式碼實現中。隨著模型的進化,程式碼實現也會進行重構,以更好的體現模型概念。

  • 主要包括:
  1. 代表領域中的概念,如實體、值物件、領域服務、模組等;

  2. 用於管理物件的生命週期。如聚合、工廠、倉庫等;

  3. 用於整合或跟蹤,如領域事件等;

3.3. 名詞解釋

  1. 領域/子域:什麼領域?從廣義上將,領域即是一個組織所做的事情以及所包含的一切,領域可大可小有界限,不是無限大,如電商領域,交易領域,購物領域等,比如我們常聽客戶說「我們有這樣幾塊業務」一般來說這裡所謂"幾塊兒"就是指子域 。

  2. 實體(entity):這個詞被我們廣泛使用,甚至過分使用,實體是一個重要的概念,一個典型實體應具備3個要素(身份標識、屬性、領域行為),必須有唯一的身份標識,沒有身份標識的領域物件就不是實體。如:User物件就是一個實體。

  3. 屬性:實體的屬性用來說明主體的靜態特徵,並持有資料與狀態。

@Data
public class Product{
    private String sku;
    private String name;
    private Price price;
}




  1. 領域行為:實體擁有領域行為,可以更好地說明其作為主體的動態特徵。一個不具備動態特徵的物件不屬於領域行為。
@Data
public class Product{
    private String sku;
    private String name;
    private Price price;

    //變更狀態的領域行為
    public void changePriceTo(Price newPrice){
        //設計產品新加個
        .......
        
    }
}





  1. 值物件(value object):比較抽象,通常作為實體的屬性,區分值物件與實體的區別在於,值物件是不可變的,並且沒有唯一標識,僅由其屬性的值定義,參與則對它的判斷是依據值還是依據身份標識,前者是值物件,後者是實體;

舉個小例子:訂單項和訂單的關係:多對一,一個訂單裡有多條訂單項,一個訂單項,只會出現在一個訂單裡,組合關係,部分不能脫離主體單獨存在

public class Order {
    int id;
    User user;
}

public class OrderItem {
    private int id;
    private Product product;
    private int num;
    private Order order;
}




6)  聚合根:聚合中需要指定一個實體作為聚合根來作為整個聚合的對外觸電,也就是說外部只能通過聚合根實現對內部物件的存取,這樣的限制可以對內部物件實現最大化的保護。

4 價值是什麼

幾乎所有專案的發展都有這樣一個規律:初期需求簡單,中後期業務激增系統複雜度升級,導致最初的設計理念需要大刀闊斧的改革,所以,系統越複雜、程式碼規模越大,DDD 的優勢就越明顯。

  • 合作溝通:強調團隊與領域專家的合作溝通,有助於建立一個溝通良好的團隊組織;
  • 統一思想:統一設計思想與設計規範,有助於提高團隊成員的架構設計能力和物件導向設計能力;
  • 系統靈活:通過建模可提高模型的高內聚,降低模型建的耦合度,提高系統的可延伸性與穩定性;
  • 產品核心:善於處理高複雜度業務產品研發,可幫助我們提煉穩定的產品核心;
  • 業務沉澱:領域模型是系統的核心,是領域內業務的直接沉澱,具有非常大的業務價值。

5 基礎篇結束語

微服務劃分的一個重要理論基礎就是領域驅動設計,但由於DDD門檻高、概念多,體系龐大又抽象,再加上實踐經驗和案例缺少,很多開發人員對DDD存在不少疑惑,或只停留在平時依靠檢索或身邊同事談及耳聞了解DDD,通過本篇初步認識了領域驅動設計、前期我們先暫短介紹這裡,後續會將從程式碼層面入手分享DDD實現落地。

作者:京東物流 邊雷

來源:京東雲開發者社群 自猿其說Tech 轉載請註明來源