領域驅動設計--戰術模式簡介

文心紫竹發表於2019-03-21

戰術模式包含若干構造塊模式,以便能夠構建有效的領域模型。

戰術模式嚴重依賴於領域模型和通用語言,通過技術模式將領域模型和通用語言中的概念對映到程式碼實現中。隨著模型的進化,程式碼實現也會進行重構,以更好的體現模型概念。 當然,從技術重構角度也會發現一些隱含領域知識(概念),這些新的發現也會對領域模型產生影響。

戰術模式和通用語言一樣,都工作在特定限界上下文內,其應用邊界受限界上下文的保護。

1 戰術模式

戰術模式的作用是管理複雜性並確保領域模型中行為的清晰明確。可以使用這些模式來捕獲和傳遞領域中的概念、關係、規則。

每個構造塊模式都具有單一職責:

  1. 代表領域中的概念。如實體、值物件、領域服務、領域事件、模組等;
  2. 用於管理物件的生命週期。如聚合、工廠、倉庫等;
  3. 用於整合或跟蹤。領域事件、事件溯源等。

戰術模式 領域模型構造塊

1.1 領域建模模式

他們表述實現與模型間的關係,將分析模型繫結到程式碼實現模型。主要用於在程式碼中表述模型元素的模式。

1.1.1 實體

實體表述的是領域中的概念,它是由身份而不是屬性來定義的。

實體的身份標識在生命週期中保持不變,但其屬性會發生變化。實體以身份標識作為唯一憑證,沿著時間軸,記錄了實體所有變更事件。

實體的一個例項是產品,一旦產品被生成好,其唯一身份就不會發生變化,但是其描述資訊、價格等可以被多次修改。

實體

1.1.2 值物件

值物件代表僅通過資料區分的領域元素和概念。用作模型中元素的描述,它不具有唯一標識。

值物件不需要唯一標識,是因為它總是與另一個物件相關聯,是在一個特定上下文中被解析的。通常,其生命週期會依附於它的關聯物件(在這裡,主要是實體物件)。

值物件會當做不變物件來設計,在完成建立後,其狀態就不能改變了。

值物件比較好的例子就是現金,你無需關係貨幣的身份,只關心它的價值。如果有人用一張五美元鈔票交換你的五張一美元鈔票,也不會改變五美元本身。

值物件

1.1.3 領域服務

在模型中,領域服務封裝了不能自然建模為值物件和實體的邏輯、流程和概念。

它本身不具有身份和狀態。它的職責是使用實體和值物件編排業務邏輯。

領域服務的一個好例子是運輸成本計算器,只要給出一組拖運貨物和重量,它就能計算運輸成本。

1.1.4 模組

模組主要用於組織和封裝相關概念(實體、值物件、領域服務、領域事件等),這樣可以簡化對較大模型的理解。

應用模組可以在領域模型中促成低耦合和搞內聚的設計。

模組作用於單個領域,用於分解模型規模。子域用於限定領域模型適用範圍(有界上下文)。

1.2 物件生命週期模式

相對來說,之前提到的模式重點在於表達領域概念。而物件生命週期模式,有點側重於技術,用於表示領域物件的建立和持久化。

1.2.1 聚合

實體和值物件會相互協作,形成複雜的關聯關係。我們需要在滿足不變條件的前提下,將其拆分為一個個概念上的整體。

通常,面對複雜的物件關係,在執行領域物件操作時,難以保證一致性和併發性。

領域驅動設計由聚合模式來確保操作的一致性和事務的併發邊界。大模型會通過不變性條件來劃分,並組成概念化整體的實體和物件組,這個概念化整體便是聚合。

聚合根之間的關係應該通過保持對另一個聚合根 ID 的引用,而非對物件本身的引用來實現。這一原則有助於保持聚合之間的邊界並避免載入不必要的物件。

不變性,是在領域模型中強制實現一致性的規則。無論何時對實體或聚合進行變更都要應用該業務規則。

聚合外部的物件只能引用另一個聚合的聚合根,聚合中物件的任何變更都需要通過聚合根來完成。聚合根封裝聚合資料並公開行為以對其進行修改。

聚合

1.2.2 工廠

如果實體或值物件的建立過程非常複雜,可以將其委託給工廠。工廠會確保在領域物件使用之前就滿足所有的不變條件。

如果領域物件很簡單並且不具有特殊的不變條件,可以使用建構函式代替工廠。當從持久化儲存中重建領域物件時,也可以使用工廠。

工廠

1.2.3 倉庫

倉庫主要用於持久化一個聚合。將聚合作為原子單元進行處理,因此,倉庫操作的最小單元就是聚合,每個聚合會對應一個倉庫。

倉庫

倉庫是用來檢索和儲存聚合的機制,是對基礎框架的一種抽象。

1.3 其他模式

1.3.1 領域事件

領域事件表示問題空間中發生了一些業務人員關心的事情。主要用於表示領域概念。

使用領域事件主要有以下兩種場景:

  1. 記錄模型的變更歷史;
  2. 作為跨聚合通訊方式。
1.3.2 事件溯源

傳統的僅快照式持久化的一個替代項便是事件溯源。作為實體狀態儲存的替代,可以儲存引發該狀態的系列事件。

儲存所有的事件會提高業務的分析能力,不僅可以得知實體當前狀態,還可以得知過去任意時點的狀態。

事件溯源

1.4 總結

  1. 實體
    • 由唯一識別符號定義
    • 識別符號在整個生命週期儲存不變
    • 基於識別符號進行相等性檢查
    • 通過方法對屬性進行更新
  2. 值物件
    • 描述問題域中的概念和特徵
    • 不具備身份
    • 不變物件
  3. 領域服務
    • 處理無法放置在實體或值物件中的領域邏輯
    • 無唯一標識
    • 無狀態服務
  4. 模組
    • 分解、組織和提高領域模型的可讀性
    • 名稱空間,降低耦合,提供模型高內聚性
    • 定義領域物件組間的邊界
    • 封裝比較獨立的概念,是比聚合、實體等更高層次的抽象
  5. 聚合
    • 將大物件圖分解成小的領域物件群,降低技術實現的複雜性
    • 表示領域概念,不僅僅是領域物件集合
    • 確定領域一致性邊界,確保領域的可靠性
    • 控制併發邊界
  6. 工廠
    • 將物件的使用和構造分離
    • 封裝複雜實體和值物件的建立邏輯
    • 保障複雜實體和值物件的業務完整性
  7. 倉庫
    • 是聚合根在記憶體中的集合介面
    • 提供聚合根的檢索和持久化需求
    • 將領域層與基礎實施層解耦
    • 通常不用於報告需求
  8. 領域事件
    • 業務人員所關心的事件,是通用語言的一部分
    • 記錄聚合根的所有變更
    • 處理跨聚合的通訊需求
  9. 事件溯源
    • 使用歷史事件記錄替換快照儲存
    • 提供對歷史狀態的查詢

相關文章