戰術模式包含若干構造塊模式,以便能夠構建有效的領域模型。
戰術模式嚴重依賴於領域模型和通用語言,通過技術模式將領域模型和通用語言中的概念對映到程式碼實現中。隨著模型的進化,程式碼實現也會進行重構,以更好的體現模型概念。 當然,從技術重構角度也會發現一些隱含領域知識(概念),這些新的發現也會對領域模型產生影響。
戰術模式和通用語言一樣,都工作在特定限界上下文內,其應用邊界受限界上下文的保護。
1 戰術模式
戰術模式的作用是管理複雜性並確保領域模型中行為的清晰明確。可以使用這些模式來捕獲和傳遞領域中的概念、關係、規則。
每個構造塊模式都具有單一職責:
- 代表領域中的概念。如實體、值物件、領域服務、領域事件、模組等;
- 用於管理物件的生命週期。如聚合、工廠、倉庫等;
- 用於整合或跟蹤。領域事件、事件溯源等。
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.3.2 事件溯源
傳統的僅快照式持久化的一個替代項便是事件溯源。作為實體狀態儲存的替代,可以儲存引發該狀態的系列事件。
儲存所有的事件會提高業務的分析能力,不僅可以得知實體當前狀態,還可以得知過去任意時點的狀態。
1.4 總結
- 實體
- 由唯一識別符號定義
- 識別符號在整個生命週期儲存不變
- 基於識別符號進行相等性檢查
- 通過方法對屬性進行更新
- 值物件
- 描述問題域中的概念和特徵
- 不具備身份
- 不變物件
- 領域服務
- 處理無法放置在實體或值物件中的領域邏輯
- 無唯一標識
- 無狀態服務
- 模組
- 分解、組織和提高領域模型的可讀性
- 名稱空間,降低耦合,提供模型高內聚性
- 定義領域物件組間的邊界
- 封裝比較獨立的概念,是比聚合、實體等更高層次的抽象
- 聚合
- 將大物件圖分解成小的領域物件群,降低技術實現的複雜性
- 表示領域概念,不僅僅是領域物件集合
- 確定領域一致性邊界,確保領域的可靠性
- 控制併發邊界
- 工廠
- 將物件的使用和構造分離
- 封裝複雜實體和值物件的建立邏輯
- 保障複雜實體和值物件的業務完整性
- 倉庫
- 是聚合根在記憶體中的集合介面
- 提供聚合根的檢索和持久化需求
- 將領域層與基礎實施層解耦
- 通常不用於報告需求
- 領域事件
- 業務人員所關心的事件,是通用語言的一部分
- 記錄聚合根的所有變更
- 處理跨聚合的通訊需求
- 事件溯源
- 使用歷史事件記錄替換快照儲存
- 提供對歷史狀態的查詢