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

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/09/06/17681594.html
-Advertisement-
Play Games

本文與大家一起學習並介紹領域驅動設計(Domain Drive Design) 簡稱DDD,以及為什麼我們需要領域驅動設計,它有哪些優缺點,儘量用一些通俗易懂文字來描述講解領域驅動設計 ...


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 轉載請註明來源


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

-Advertisement-
Play Games
更多相關文章
  • 當談到[數據湖](https://www.dtstack.com/dtengine/easylake?src=szsm)的時候,大家都在說,可以把所有數據(結構化/半結構化/非結構化)一股腦都丟進去,進行統一的元數據管理。然後上層計算對接,進行[流批計算](https://www.dtstack.c ...
  • 浙江省工業和信息化廳開展了2023第二季度創新型中小企業評價工作,玖章算術以優秀的自主創新能力通過認定,成為浙江省2023年度創新型中小企業。玖章算術聚焦於雲計算與數據管理基礎技術領域,擁有豐富的研發經驗和專業技術團隊。NineData是新一代的雲原生智能數據管理平臺,包含了數據複製、SQL開發、數... ...
  • `` 數組的includes方法在日常的編程中比較常用到,其作用就是判斷某一數據是否在數組中,通常來說,數組中的數據如果是數字,布爾值,或者字元串的話,都是能夠進行判斷的 例如: ``` [1,2,3,4].includes(3) // true [1,2,3,4].includes(5) // f ...
  • 好家伙, 1.<template>去哪了 在正式內容之前,我們來思考一個問題, 當我們使用vue開發頁面時,<tamplete>中的內容是如何變成我們網頁中的內容的? 它會經歷四步: 解析模板:Vue會解析<template>中的內容,識別出其中的指令、插值表達式({{}}),以及其他元素和屬性。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一.@click和@click.native的區別 vue @click.native 原生點擊事件: 1,給vue組件綁定事件時候,必須加上native ,不然不會生效(監聽根元素的原生事件,使用 .native 修飾符) 2,等同於在 ...
  • 在vue3中,可以使用vue3的API `defineExpose()`函數結合`ref`或者`$parent`,實現父子組件數據的傳遞。 # 子組件向父組件傳遞數據`defineExpose()`和`ref` - 子組件:通過`defineExpose()` 函數,向外暴露響應式數據或者方法 `` ...
  • 本文給大家介紹了什麼是"編程範式",選擇合適的編程範式可以提高代碼的可讀性、可維護性和可擴展性。 一、 什麼是編程範式? "編程範式"是一種編程思想的總稱,它是指在編寫程式時所採用的基本方法和規範。常見的編程範式有面向對象、函數式、邏輯式等。 選擇合適的編程範式可以提高代碼的可讀性、可維護性和可擴展 ...
  • ### 工廠模式 工廠模式是一種創建者設計模式,細分之下可以分成三類`簡單工廠模式`,`工廠方法模式`和`抽象工廠模式`。 #### 簡單工廠模式 最簡單的工廠模式,它採用靜態方法的方式來決定應該應該生產什麼商品。 ```java public class StoreFactory { public ...
一周排行
    -Advertisement-
    Play Games
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...