【DDD】學習筆記-實體和值對象:從領域模型的基礎單元看系統設計

今天我們來學習 DDD 戰術設計中的兩個重要概念:實體和值對象。

這兩個概念都是領域模型中的領域對象。它們在領域模型中起什么作用,戰術設計時如何將它們映射到代碼和數據模型中去?就是我們這一講重點要關注的問題。

另外,在戰略設計向戰術設計過渡的這個過程中,理解和區分實體和值對象在不同階段的形態是很重要的,畢竟階段不同,它們的形態也會發生變化,這與我們的設計和代碼實現密切相關。

接下來,我們就分別看看實體和值對象的這些問題,從中找找答案。

實體

我們先來看一下實體是什么東西?

在 DDD 中有這樣一類對象,它們擁有唯一標識符,且標識符在歷經各種狀態變更后仍能保持一致。對這些對象而言,重要的不是其屬性,而是其延續性和標識,對象的延續性和標識會跨越甚至超出軟件的生命周期。我們把這樣的對象稱為實體。沒理解?沒關系!請繼續閱讀。

1. 實體的業務形態

在 DDD 不同的設計過程中,實體的形態是不同的。在戰略設計時,實體是領域模型的一個重要對象。領域模型中的實體是多個屬性、操作或行為的載體。在事件風暴中,我們可以根據命令、操作或者事件,找出產生這些行為的業務實體對象,進而按照一定的業務規則將依存度高和業務關聯緊密的多個實體對象和值對象進行聚類,形成聚合。你可以這么理解,實體和值對象是組成領域模型的基礎單元。

2. 實體的代碼形態

在代碼模型中,實體的表現形式是實體類,這個類包含了實體的屬性和方法,通過這些方法實現實體自身的業務邏輯。在 DDD 里,這些實體類通常采用充血模型,與這個實體相關的所有業務邏輯都在實體類的方法中實現,跨多個實體的領域邏輯則在領域服務中實現。

3. 實體的運行形態

實體以 DO(領域對象)的形式存在,每個實體對象都有唯一的 ID。我們可以對一個實體對象進行多次修改,修改后的數據和原來的數據可能會大不相同。但是,由于它們擁有相同的 ID,它們依然是同一個實體。比如商品是商品上下文的一個實體,通過唯一的商品 ID 來標識,不管這個商品的數據如何變化,商品的 ID 一直保持不變,它始終是同一個商品。

4. 實體的數據庫形態

與傳統數據模型設計優先不同,DDD 是先構建領域模型,針對實際業務場景構建實體對象和行為,再將實體對象映射到數據持久化對象。

在領域模型映射到數據模型時,一個實體可能對應 0 個、1 個或者多個數據庫持久化對象。大多數情況下實體與持久化對象是一對一。在某些場景中,有些實體只是暫駐靜態內存的一個運行態實體,它不需要持久化。比如,基于多個價格配置數據計算后生成的折扣實體。

而在有些復雜場景下,實體與持久化對象則可能是一對多或者多對一的關系。比如,用戶 user 與角色 role 兩個持久化對象可生成權限實體,一個實體對應兩個持久化對象,這是一對多的場景。再比如,有些場景為了避免數據庫的聯表查詢,提升系統性能,會將客戶信息 customer 和賬戶信息 account 兩類數據保存到同一張數據庫表中,客戶和賬戶兩個實體可根據需要從一個持久化對象中生成,這就是多對一的場景。

值對象

值對象相對實體來說,會更加抽象一些,概念上我們會結合例子來講。

我們先看一下《實現領域驅動設計》一書中對值對象的定義:通過對象屬性值來識別的對象,它將多個相關屬性組合為一個概念整體。在 DDD 中用來描述領域的特定方面,并且是一個沒有標識符的對象,叫作值對象。

也就說,值對象描述了領域中的一件東西,這個東西是不可變的,它將不同的相關屬性組合成了一個概念整體。當度量和描述改變時,可以用另外一個值對象予以替換。它可以和其它值對象進行相等性比較,且不會對協作對象造成副作用。這部分在后面講“值對象的運行形態”時還會有例子。

上面這兩段對于定義的闡述,如果你還是覺得有些晦澀,我們不妨“翻譯”一下,用更通俗的語言把定義講清楚。

簡單來說,值對象本質上就是一個集。那這個集合里面有什么呢?若干個用于描述目的、具有整體概念和不可修改的屬性。那這個集合存在的意義又是什么?在領域建模的過程中,值對象可以保證屬性歸類的清晰和概念的完整性,避免屬性零碎。

這里我舉個簡單的例子,請看下面這張圖:

img

人員實體原本包括:姓名、年齡、性別以及人員所在的省、市、縣和街道等屬性。這樣顯示地址相關的屬性就很零碎了對不對?現在,我們可以將“省、市、縣和街道等屬性”拿出來構成一個“地址屬性集合”,這個集合就是值對象了。

1. 值對象的業務形態

值對象是 DDD 領域模型中的一個基礎對象,它跟實體一樣都來源于事件風暴所構建的領域模型,都包含了若干個屬性,它與實體一起構成聚合。

我們不妨對照實體,來看值對象的業務形態,這樣更好理解。本質上,實體是看得到、摸得著的實實在在的業務對象,實體具有業務屬性、業務行為和業務邏輯。而值對象只是若干個屬性的集合,只有數據初始化操作和有限的不涉及修改數據的行為,基本不包含業務邏輯。值對象的屬性集雖然在物理上獨立出來了,但在邏輯上它仍然是實體屬性的一部分,用于描述實體的特征。

在值對象中也有部分共享的標準類型的值對象,它們有自己的限界上下文,有自己的持久化對象,可以建立共享的數據類微服務,比如數據字典。

2. 值對象的代碼形態

值對象在代碼中有這樣兩種形態。如果值對象是單一屬性,則直接定義為實體類的屬性;如果值對象是屬性集合,則把它設計為 Class 類,Class 將具有整體概念的多個屬性歸集到屬性集合,這樣的值對象沒有 ID,會被實體整體引用。

我們看一下下面這段代碼,person 這個實體有若干個單一屬性的值對象,比如 Id、name 等屬性;同時它也包含多個屬性的值對象,比如地址 address。

img

3. 值對象的運行形態

實體實例化后的 DO 對象的業務屬性和業務行為非常豐富,但值對象實例化的對象則相對簡單和乏味。除了值對象數據初始化和整體替換的行為外,其它業務行為就很少了。

值對象嵌入到實體的話,有這樣兩種不同的數據格式,也可以說是兩種方式,分別是屬性嵌入的方式和序列化大對象的方式。

引用單一屬性的值對象或只有一條記錄的多屬性值對象的實體,可以采用屬性嵌入的方式嵌入。引用一條或多條記錄的多屬性值對象的實體,可以采用序列化大對象的方式嵌入。比如,人員實體可以有多個通訊地址,多個地址序列化后可以嵌入人員的地址屬性。值對象創建后就不允許修改了,只能用另外一個值對象來整體替換。

如果你對這兩種方式不夠了解,可以看看下面的例子。

案例 1:以屬性嵌入的方式形成的人員實體對象,地址值對象直接以屬性值嵌入人員實體中。

img

案例 2:以序列化大對象的方式形成的人員實體對象,地址值對象被序列化成大對象 Json 串后,嵌入人員實體中。

img

4. 值對象的數據庫形態

DDD 引入值對象是希望實現從“數據建模為中心”向“領域建模為中心”轉變,減少數據庫表的數量和表與表之間復雜的依賴關系,盡可能地簡化數據庫設計,提升數據庫性能。

如何理解用值對象來簡化數據庫設計呢?

傳統的數據建模大多是根據數據庫范式設計的,每一個數據庫表對應一個實體,每一個實體的屬性值用單獨的一列來存儲,一個實體主表會對應 N 個實體從表。而值對象在數據庫持久化方面簡化了設計,它的數據庫設計大多采用非數據庫范式,值對象的屬性值和實體對象的屬性值保存在同一個數據庫實體表中。

舉個例子,還是基于上述人員和地址那個場景,實體和數據模型設計通常有兩種解決方案:第一是把地址值對象的所有屬性都放到人員實體表中,創建人員實體,創建人員數據表;第二是創建人員和地址兩個實體,同時創建人員和地址兩張表。

第一個方案會破壞地址的業務涵義和概念完整性,第二個方案增加了不必要的實體和表,需要處理多個實體和表的關系,從而增加了數據庫設計的復雜性。

那到底應該怎樣設計,才能讓業務含義清楚,同時又不讓數據庫變得復雜呢?

我們可以綜合這兩個方案的優勢,揚長避短。在領域建模時,我們可以把地址作為值對象,人員作為實體,這樣就可以保留地址的業務涵義和概念完整性。而在數據建模時,我們可以將地址的屬性值嵌入人員實體數據庫表中,只創建人員數據庫表。這樣既可以兼顧業務含義和表達,又不增加數據庫的復雜度。

值對象就是通過這種方式,簡化了數據庫設計,總結一下就是:在領域建模時,我們可以將部分對象設計為值對象,保留對象的業務涵義,同時又減少了實體的數量;在數據建模時,我們可以將值對象嵌入實體,減少實體表的數量,簡化數據庫設計。

另外,也有 DDD 專家認為,要想發揮對象的威力,就需要優先做領域建模,弱化數據庫的作用,只把數據庫作為一個保存數據的倉庫即可。即使違反數據庫設計原則,也不用大驚小怪,只要業務能夠順利運行,就沒什么關系。

5. 值對象的優勢和局限

值對象是一把雙刃劍,它的優勢是可以簡化數據庫設計,提升數據庫性能。但如果值對象使用不當,它的優勢就會很快變成劣勢。“知彼知己,方能百戰不殆”,你需要理解值對象真正適合的場景。

值對象采用序列化大對象的方法簡化了數據庫設計,減少了實體表的數量,可以簡單、清晰地表達業務概念。這種設計方式雖然降低了數據庫設計的復雜度,但卻無法滿足基于值對象的快速查詢,會導致搜索值對象屬性值變得異常困難。

值對象采用屬性嵌入的方法提升了數據庫的性能,但如果實體引用的值對象過多,則會導致實體堆積一堆缺乏概念完整性的屬性,這樣值對象就會失去業務涵義,操作起來也不方便。

所以,你可以對照著以上這些優劣勢,結合你的業務場景,好好想一想了。那如果在你的業務場景中,值對象的這些劣勢都可以避免掉,那就請放心大膽地使用值對象吧。

實體和值對象的關系

實體和值對象是微服務底層的最基礎的對象,一起實現實體最基本的核心領域邏輯。

值對象和實體在某些場景下可以互換,很多 DDD 專家在這些場景下,其實也很難判斷到底將領域對象設計成實體還是值對象?可以說,值對象在某些場景下有很好的價值,但是并不是所有的場景都適合值對象。你需要根據團隊的設計和開發習慣,以及上面的優勢和局限分析,選擇最適合的方法。

關于值對象,我還要多說幾句。其實,DDD 引入值對象還有一個重要的原因,就是到底領域建模優先還是數據建模優先?

DDD 提倡從領域模型設計出發,而不是先設計數據模型。前面講過了,傳統的數據模型設計通常是一個表對應一個實體,一個主表關聯多個從表,當實體表太多的時候就很容易陷入無窮無盡的復雜的數據庫設計,領域模型就很容易被數據模型綁架。可以說,值對象的誕生,在一定程度上,和實體是互補的。

我們還是以前面的圖示為例:

img

在領域模型中人員是實體,地址是值對象,地址值對象被人員實體引用。在數據模型設計時,地址值對象可以作為一個屬性集整體嵌入人員實體中,組合形成上圖這樣的數據模型;也可以以序列化大對象的形式加入到人員的地址屬性中,前面表格有展示。

從這個例子中,我們可以看出,同樣的對象在不同的場景下,可能會設計出不同的結果。有些場景中,地址會被某一實體引用,它只承擔描述實體的作用,并且它的值只能整體替換,這時候你就可以將地址設計為值對象,比如收貨地址。而在某些業務場景中,地址會被經常修改,地址是作為一個獨立對象存在的,這時候它應該設計為實體,比如行政區劃中的地址信息維護。

總結

今天我們主要學習了實體和值對象在 DDD 不同設計階段的形態,以及它們從戰略設計向戰術設計演進過程中的設計方法。

這個過程是從業務模型向系統模型落地的過程,比較復雜,很考驗你的設計能力,很多時候我們都要結合自己的業務場景,選擇合適的方法來進行微服務設計。強調一點,我們不避諱傳統的設計方法,畢竟適合自己的才是最好的。希望你能充分理解實體和值對象的概念和應用,將學到的知識復用,最終將適合自己業務的 DDD 設計方法納入到架構體系,實現落地。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/715517.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/715517.shtml
英文地址,請注明出處:http://en.pswp.cn/news/715517.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

springboot238光影視頻

光影視頻平臺 摘 要 使用舊方法對光影視頻平臺的信息進行系統化管理已經不再讓人們信賴了,把現在的網絡信息技術運用在光影視頻平臺的管理上面可以解決許多信息管理上面的難題,比如處理數據時間很長,數據存在錯誤不能及時糾正等問題。這次開…

APS面試審核準備的常規問題

之前根據其他人的經驗貼,準備了一些可能APS 面試審核可能會遇到的常規問題,現在簡單分享一下。 一般會考慮到留學資金來源,在德國能不能順利畢業;學的是什么專業內容之類的,判斷去德國會不會好好學習;對德國…

Linux:上傳文件到虛擬機

常見的方法: 使用虛擬機軟件提供的文件共享功能: 對于VMware Workstation,可以使用“共享文件夾”功能。對于VirtualBox,可以使用“共享文件夾”或“拖放”功能。 使用網絡文件共享服務: 您可以在虛擬機中配置一個Sam…

【Python入門教程】Python實現雞兔同籠

今天跟大家分享一下很久之前自己做的雞兔同籠求解問題的小游戲,使用公式和基本的判斷語句即可實現,可以用來當練手或者消磨時間用。 大家在編代碼的時候最重要就是先理清邏輯思路,例如應該套幾層循環、分幾個模塊等等。然后在編碼時可以先隨意…

TS中符號的用法:?、??、 !、 !!

1) ? 的用法 示例: const obj res?.data || {}; // obj是從接口中取到的數據const dataError obj.a.b; // 若obj為空,則此時會報錯const dataSafe obj?.a?.b; // 相當于 const dataSafe obj && obj.a && obj.a.b ? obj.a.b…

wy的leetcode刷題記錄_Day80

wy的leetcode刷題記錄_Day80 聲明 本文章的所有題目信息都來源于leetcode 如有侵權請聯系我刪掉! 時間:2024-3-2 前言 目錄 wy的leetcode刷題記錄_Day80聲明前言2368. 受限條件下可到達節點的數目題目介紹思路代碼收獲 92. 反轉鏈表 II題目介紹思路代碼收獲 2368…

Redis持久化+Redis內存管理和優化+Redis三大緩存問題

Redis持久化Redis內存管理和優化Redis三大緩存問題一、Redis高可用二、Redis持久化1、RDB持久化1.1 觸發條件(1) 手動觸發(2) 自動觸發(3) 其他自動觸發機制 1.2 執行流程1.3 啟動時加載 2、AOF持久化2.1 開啟AOF2.2 執行流程(1) 命令追加(append)(2) 文件寫入(write)和文件同步…

讀書筆記-三國演義-荊州爭奪

荊州爭奪 赤壁之戰后,荊州成為蜀漢、曹魏和孫吳三方爭奪的焦點。劉備、曹操和孫權相繼占據荊州,展開了一系列激烈的軍事沖突和政治斗爭。 赤壁之戰后的荊州爭奪是三國時期曹操、劉備和孫權之間的一場激烈競爭,是繼赤壁之戰后三方勢力之間的…

網絡編程筆記

網絡編程 1.網絡編程常用工具 1.掃描器 每一個網絡編程者手中都有一兩個用得順手的掃描器,掃描器在一個老練的網絡編程者手里有著相當大的作用。利用掃描器,網絡編程者可以對某一網段的機器或是某臺目標機器進行快速漏洞掃描,因為傳統的手…

langchain學習筆記(十)

Bind runtime args | 🦜?🔗 Langchain 1、有時,我們希望使用常量參數調用Runnable序列中的Runnable,這些參數不是序列中前一個Runnable的輸出的一部分,也不是用戶的輸入,這時可以用Runnable.bind() from …

關于synchronized介紹

synchronized的特性 1. 樂觀鎖/悲觀鎖自適應,開始時是樂觀鎖,如果鎖沖突頻繁,就轉換為悲觀鎖 2.輕量級/重量級鎖自適應 開始是輕量級鎖實現,如果鎖被持有的時間較長,就轉換成重量級鎖 3.自旋/掛起等待鎖自適應 4.不是讀寫鎖 5.非公平鎖 6,可重入鎖 synchronized的使用 1&#…

2024家用洗地機品牌推薦!洗地機選什么牌子好?建議選擇這幾款

如今生活節奏加快,工作繁忙的上班族很少有時間做家務。即使抽出時間打掃,也難以保持家庭長久干凈整潔。許多人聽說了智能化家居神器——洗地機,想要入手一臺。但在市場上各種洗地機層出不窮,很多人不知如何選擇。下面是我給大家整…

掌握MyBatis:輕松解鎖數據庫操作的藝術

MyBatis是一款優秀的持久層框架,它封裝了JDBC操作的很多繁瑣細節,提供了一種相對簡便的操作數據庫的方法。MyBatis通過XML描述接口綁定的SQL語句,以及通過Java注解的方式,將Java對象與數據庫表進行映射,從而簡化了數據…

降低85%的gc發生率:ES的GC調優實踐!

#大數據/ES #經驗 #性能 ES的服務日志出現一些gc overhead現象,經過調優對比,gc發生率顯著下降了85%,分享參數如下: ES的G1GC參數(多實例) -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:InitiatingHeapOccu…

Redis緩存雙寫一致性之更新策略

文章目錄 1. 經典面試題2. 雙寫一致性3. 更新策略4. canal簡介5. Redis與Mysql數據雙寫一致性工程落地案例 1. 經典面試題 上面的業務邏輯你用java代碼如何實現?你只要用緩存,就可能會涉及到redis緩存與數據庫雙存儲雙寫,你只要是雙寫&#x…

嵌入式學習day29 指針復習

1.指針: 1.提供一種間接訪問數據的方法 2.空間沒有名字,只有一個地址編號 2.指針: 1.地址:區分不同內存空間的編號 2.指針:指針就是地址,地址就是指針 3.指針變量:存放指針的變量稱為指針變量,簡稱為指針 3.指針的定義: int *p NULL; …

MyBatis中 #{} 和 ${} 區別

Mybatis的Mapper映射文件中,有兩種方式可以引用形參變量進行取值: #{} 和 ${}。本文將簡述兩種方式的區別和適用場景 取值引用 #{} 方式 #{}: 解析為SQL時,會將形參變量的值取出,并自動給其添加引號。 例如:當實參username&quo…

AI 筆記助手,你的思路整理助手

大家好,今天給大家介紹一款非常實用的 AI 筆記助手——AI Note。這款助手就像是一個貼心的小助手,能幫助我們整理筆記,提高學習和工作效率。 🤖 AI Note 可以智能總結筆記內容,準確標記重點,讓我們更快地獲…

final關鍵字有什么作用

final關鍵字在Java中用于聲明變量、方法和類,表示它們的值或行為不能被修改。 被 final 修飾的類不可以被繼承 被 final 修飾的方法不可以被重寫 被 final 修飾的變量不可變,被 final 修飾的變量必須被顯式第指定初始值,還得注意的是&#…

學習助手:借助AI大模型,學習更高效!

在當今的數字時代,人工智能(AI)的崛起已經徹底改變了我們獲取信息、處理數據以及學習新知識的方式。AI大模型,特別是如OpenAI開發的GPT-4這類先進的技術,已成為學習和教育領域的一大助力。本文旨在探索如何借助AI大模型…