【DDD】持久化領域對象的方法實踐

来源:https://www.cnblogs.com/uoyo/archive/2020/01/08/12167360.html
-Advertisement-
Play Games

雖然領域驅動設計的思想很誘人,但我們依然會面臨各種隱藏的困難,就比如今天我們要講的主題“持久化”:即使前期我們設計了足夠完整的領域對象,但是依然需要持久化它們到資料庫中,而普通的關係型資料庫可能很難維持領域對象的原有結構,所以我們必須要使用一些特有的手段來處理它。將值對象持久化成欄位好呢?還是將值對... ...


目錄

概述

在實踐領域驅動設計(DDD)的過程中,我們會根據項目的所在領域以及需求情況捕獲出一定數量的領域對象。設計得足夠好的領域對象便於我們更加透徹的理解業務,方便系統後期的擴展和維護,不至於隨著需求的擴展和代碼量的累積,系統逐漸演變為大泥球(Big Ball of Mud)。

雖然領域驅動設計的思想很誘人,但我們依然會面臨各種隱藏的困難,就比如今天我們要講的主題“持久化”:即使前期我們設計了足夠完整的領域對象,但是依然需要持久化它們到資料庫中,而普通的關係型資料庫可能很難維持領域對象的原有結構,所以我們必須要使用一些特有的手段來處理它。

開篇

本篇文章屬於《如何運用領域驅動設計》系列的一個補充,如果您閱讀過該系列的其它文章,您就會發現關於“持久化”的這個問題已經不止在一篇博文中提及到了。

那麼,到底是什麼原因讓我們面臨這個問題呢? 是的!值對象! 如果您認真的瞭解過值對象的話(如果還不瞭解值對象,您可以參考 如何運用領域驅動設計 - 值對象),您會發現值對象是由許多基元類型構成的(比如string,int,double等),所以我們可以理解它為對細粒度基元類型的包裹,構成我們所在領域中的一個基礎類型,比如說下麵這個例子:

public sealed class City : ValueObject
{
    public string Name { get; }
    public int Population { get; }

    public City(string name, int population)
    {
        Name = name;
        Population = population;
    }
}

我們假設現在有一個叫做City的值對象,它是由名稱(Name)和人口數量(Population)構成。通常我們這樣建立值對象的原因很簡單,在該領域中我們一聯繫到“人口”數量就會和“城市”連同在一起(你不會說我想知道人口數量,而你會說我想知道紐約的人口數量),所以“城市”這一概念成為我們該領域中的小顆粒對象,而該對象在代碼實現中是由多個小基元類型構成的,比如該例子就是由一個string和一個int。

這樣建模的好處之一就是我們考慮的問題是一個整體,將零碎的點構建為一個整體對象,如果該對象的行為需要發生改變,只需要修改該對象本身就可以了,而不是代碼散落在各處需要到處查找(這也是滾成大泥球的原因之一)。

如果您喜歡捕獵有關DDD的知識,您可能不止一次會看到這樣一條建議規則:

In the world of DDD, there’s a well-known guideline that you should prefer Value Objects over Entities where possible. If you see that a concept in your domain model doesn’t have its own identity, choose to treat that concept as a Value Object.

該建議的內容就是提倡DDD實踐者多使用值對象。當然也不是說無論什麼東西都建立成值對象,只是要我們多去發現領域中的值對象。

但是這往往給持久化帶來了難度,先來想一下傳統的編碼持久化方式:一個對象(或者POCO)裡面包含了各個基元類型的屬性,當需要持久化時,每個屬性都對應資料庫的一個欄位,而該對象就成為了一個表。 但是這在領域驅動設計中就不好使用了,值對象成了我們考慮問題的小顆粒,而它在代碼中成了一個類,如果直接持久化它是什麼樣子呢?表,使用它的實體或者聚合根也是一個表,兩個表通過主外鍵關係鏈接。

那麼這樣持久化方式好不好呢? 答案是不確定的,可能瞭解了下文的這些方案後,您會有自己的見解。

本篇文章的持久化方案都是基於關係型資料庫,如果您是非關係型資料庫(比如mongodb),那麼您應該不會面臨這樣的問題。

欄位 Or 表

將值對象持久化成欄位好呢?還是將值對象持久化為表好呢? 這個問題其實也有很多廣泛的討論,就好比.NET好還是Java好(好吧,我php天下**),目前其實也沒有個明確的結果:

  • 覺得持久化為表欄位的原因是 如果持久化為表,必須給表添加一個ID供引用的實體或者聚合關聯,這就不滿足值對象不應該有ID的準則了。
  • 覺得持久化為表的原因是 數據表模型並不代表代碼層面的模型,代碼裡面的值對象其實並沒有ID的說法,所以它是符合值對象的,而持久化為欄位的話,同一個值對象數據會被覆製為多份導致數據冗餘。

當然哈,各有各的道理,我們也不用特別偏向於使用哪個結論。應該站在客觀的角度,實際的項目需要哪種手段就根據切實的情況來選擇。

來說一下持久化為欄位的情況

該手段其實在近期來說比較流行,特別是在EFCore2.0之後,為什麼呢?因為EF Core2.0提供了一個叫做 從屬實體類型 的概念,其實這個技術手段在EF中很早就有了,在EF中有一個叫做Complex的東西,只是在EF Core 1.x時代沒有引入而已。

在EFCore引入了Owned之後,微軟那個最著名的微服務教程 eShopOnContainers 也順勢推出了用於該特性來持久化值對象的方案:

x

所以這也是為什麼大家都在使用Owned持久化值對象的原因。(當然,大家項目中只有Address被建立為值對象的習慣不知道是不是從這兒養成的

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

-Advertisement-
Play Games
更多相關文章
  • 這篇文章主要是介紹生成器和IO多路復用機制, 算是學習asyncio需要的預備知識. 這個系列還有另外兩篇文章: 從零開始學asyncio(中) 從零開始學asyncio(下) 一. 簡單爬蟲實例 首先創建一個crawler.py文件, 寫入以下代碼: import socket req = 'GE ...
  • 目的:修改VS Code的註釋文本顏色 S1:假設VS Code的安裝路徑是 %MVSC% S2:文件資源管理器進入目錄 %MVSC%\resources\app\extensions\ S3:該目錄底下由若幹以“theme-”開頭的目錄,例如: theme-abyss theme-defaults ...
  • 控制台錯誤提示: 2020-01-08 18:34:40,292 [http-nio-8080-exec-3] [org.apache.struts2.dispatcher.Dispatcher]-[WARN] Could not find action or result: /views/dire ...
  • 背景 程式在發佈部署時候,設置環境 不生效,也沒在代碼里使用 ,啟動一直是 .最後測試發現只有在 中配置 才生效,網上找了半天資料也沒看到有什麼問題。 最終翻看源代碼,發現是在 中的 替換了全局 導致。 平時開發大體知道程式啟動時候埠啟用順序是 環境變數 預設,具體是怎麼確定使用哪個配置的,沒找到 ...
  • asp.net core 實現支持多語言 Intro 最近有一個外國友人通過郵件聯繫我,想用我的活動室預約,但是還沒支持多語言,基本上都是寫死的中文,所以最近想支持一下更多語言,於是有了多語言方面的一些實踐 國際化/本地化介紹 國際化(Globalization)和本地化(Localization) ...
  • 上篇我們說到。編寫控制器類的步驟可總結為兩個:實現一個類,然後在該類中添加一些公有方法,在運行的該類的時候可作為控制器發現,而這些方法則作為操作被髮現。 這裡我們有兩個細節: 1:系統如何知道實例化那個控制器 2:如何確定用那個方法。 路由: 1:被傳統的路由發現,2:通過特性路由發現,3:通過混合 ...
  • GRPC 是谷歌發佈的一個開源、高性能、通用RPC服務,儘管大部分 RPC 框架都使用 TCP 協議,但其實 UDP 也可以,而 gRPC 乾脆就用了 HTTP2。還有就是它具有跨平臺、跨語言 等特性,這裡就不再說明RPC是啥。 在寫項目當中,grp服務過多會非常頭疼,那麼我們分析一下如果解決這個問 ...
  • C# Excel導出超出65536行報錯 Invalid row number (65536) outside allowable range (0..65535) ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...