DDD理論學習系列(11) 工廠

2022-01-09 22:36:01 字數 3586 閱讀 3119

ddd理論學習系列——案例及目錄

在針對大型的複雜領域進行建模時,聚合、實體和值物件之間的依賴關係可能會變得十分複雜。在某個物件中為了確保其依賴物件的有效例項被建立,需要深入了解物件例項化邏輯,我們可能需要載入其他相關物件,且可能為了保持其他物件的領域不變性增加了額外的業務邏輯,這樣即打破了領域的單一責任原則(srp),又增加了領域的複雜性。

那如何去建立複雜的領域物件呢?因為複雜的領域物件的生命週期可能需要協調才能進行建立。 這個時候,我們就可以引入建立類模式——工廠模式來幫忙,將物件的使用與建立分開,將物件的建立邏輯明確地封裝到工廠物件中去。

我們有必要先理清工廠和工廠模式。

ddd中工廠的主要目標是隱藏物件的複雜建立邏輯;次要目標就是要清楚的表達物件例項化的意圖。

而工廠模式是計模式中的建立類模式之一。借助工廠模式我們可以很好實現ddd中領域物件的建立。

而針對工廠模式的實現主要有四種方式:

具體實現可以參考建立相似物件,就交給『工廠模式』吧。

當需要為聚合新增元素時,我們不能暴露聚合的結構。我們以新增商品到購物車為例,來講解如何一步一步的使用工廠模式。

一般來說,新增到購物車需要幾個步驟:

載入使用者購物車

獲取商品稅率

建立新的購物車子項

public class addproducttobasket

}}在以上**中,應用服務需要了解如何建立basketitem(購物車子項)的詳細邏輯。而這不應該時應用服務的職責,應用服務的職責在於協調。我們嘗試做以下改變來避免暴露聚合的內部結構。

public class addproducttobasket

}}namespace domainmodel }}

}以上**展示了basket(購物車)物件提供乙個add方法,用來完成新增商品到購物車的業務邏輯,對應用服務隱藏了購物車如何儲存商品的細節。另外購物車聚合能夠確保其內部集合的完整性,因為它可以確保領域的不變性。通過這種方式,完成了職責的切換,現在的應用服務要簡單的多。

然而,卻引入了乙個新的問題。為了根據商品建立有效的購物車子項,購物車需要提供乙個有效的稅率。為了建立這個稅率,它要依賴乙個taxrateservice(稅率服務)。獲取建立購物車子項依賴的稅率,這並不屬於購物車的職責。而按照上面的實現,購物車承擔了第二責任,因為它必須始終了解如何建立有效的購物車子項以及在**去獲取有效的稅率。

為了避免購物車承擔額外的職責和隱藏購物車子項的內部結構。下面我們引入乙個工廠物件來封裝購物車子項的建立,包括獲取正確的稅率。

namespace domainmodel 

}public class basketitemfactory

}}

引入工廠模式後,購物車的職責單一了,且隔離了來自購物車子項的變化,比如當稅率變化時,或購物車子項需要其他資訊建立時,都不會影響到購物車的相關邏輯。

考慮這樣的需求:訂單建立成功後,進行發貨處理時,要求根據訂單的商品和收件人資訊選擇合適的快遞方式。比如預設發順豐,順豐無法送達的選擇中國郵政。

根據這個需求,我們可以抽象出乙個kuaidi(快遞)物件用來封裝快遞資訊,和乙個delivery(發貨)物件用來封裝發貨資訊(貨物、收件人資訊、快遞等)。建立delivery的職責我們可以放到order中去,但針對order來說它並不知道要建立(選擇)哪一種kuaidi(快遞)。所以,我們可以建立乙個kuaidifactory工廠負責kuaidi物件的建立。

namespace domainmodel 

}public class kuaidifactory else }}

}

如上**所示,工廠類中我們封裝了快遞的選擇邏輯。

當要建立的物件型別有多個選擇,且客戶端並不關心建立型別的選擇時,我們可以在領域層使用工廠中去定義邏輯去決定要建立物件的型別。

提到工廠,並不是都需要需要建立獨立的工廠類來負責物件的建立。乙個工廠方法也可以存在於乙個聚合中。

比如這樣一項需求,顧客可以將購物車中的商品移到願望清單中去。

第一,這個動作是發生在購物車上的,所以我們可以毫不猶豫的在購物車中定義該行為。第二,將商品新增到願望清單中去,就需要建立乙個願望清單子項。

namespace domainmodel }}

}

從上面可以看出basket暴露乙個方法用於將basketitem轉換為wishlistitem。返回的wishlistitemwishlist聚合根的實體。另外一點我們之所以在basket中呼叫工廠去建立wishlistitem物件,是因為basket包含了建立願望清單子項所需的全部資訊。在建立了wishlistitem之後,對於basket物件來說它的任務就完成了。

在專案中,如果沒有借助orm進行資料模型與領域模型之間的對映,或者通過web服務從乙個老舊系統中獲取領域物件,都需要我們對領域物件進行重建以滿足領域的不變性。使用工廠來重建領域物件相對來說要比直接建立要複雜。

考慮這樣的場景:顧客可以在已購訂單中點選再次購買按鈕,所有訂單項全部重新新增到購物車中去。

這個場景就屬於購物車物件的重建,跟直接建立購物車物件就不同了。因為將訂單中的所有子項恢復到購物車中去,我們就需要額外確保領域的不變性。比如訂單子項對應的商品現在是否下架,如果下架我們是直接丟擲異常,還是仍舊建立乙個鎖定的購物車子項,標記其為已下架狀態?

namespace domainmodel 

}namespace domainmodel else

}// .....

return basket;}}

}

物件建立不是乙個領域的關注點,但它確實存在於應用程式的領域層中。通過使用工廠可以有效的保證領域模型的乾淨整潔,以確保領域模型的對現實的準確表達。使用工廠具有以下好處:

工廠將領域物件的使用和建立分離。

通過使用工廠類,可以隱藏建立複雜領域物件的業務邏輯。

工廠類可以根據呼叫者的需要,建立相應的領域物件。

工廠方法可以封裝聚合的內部狀態。

然而,並不是任何需要例項化物件的地方都要使用工廠。只有當用工廠比使用建構函式更有表現力時,或存在多個建構函式容易造成混淆時,或者對要建立物件所依賴的物件不關心時,才選用工廠進行物件的建立。

參考資料:

《patterns, principles, and practices of domain-driven design》

DDD理論學習系列 案例及目錄

目錄ddd理論學習系列 1 通用語言 ddd理論學習系列 2 領域 ddd理論學習系列 3 限界上下文 ddd理論學習系列 4 領域模型 ddd理論學習系列 5 統一建模語言 ddd理論學習系列 6 實體 ddd理論學習系列 7 值物件 ddd理論學習系列 8 應用服務 領域服務 ddd理論學習系列...

DDD理論學習系列(4) 領域模型

ddd理論學習系列目錄 我們還是先來拆詞理解,領域模型可以拆為 領域 和 模型 二詞。把兩個詞結合起來,我們給領域模型下個定義 領域模型是對我們軟體系統中要解決問題的抽象表達。這個理解還是很生澀,沒關係,容我娓娓道來。我們知道,軟體開發過程主要包括 需求分析 概要設計 詳細設計 編碼 測試 軟體交付...

DDD理論學習系列 案例及目錄

目錄ddd理論學習系列 1 通用語言 ddd理論學習系列 2 領域 ddd理論學習系列 3 限界上下文 ddd理論學習系列 4 領域模型 ddd理論學習系列 5 統一建模語言 ddd理論學習系列 6 實體 ddd理論學習系列 7 值物件 ddd理論學習系列 8 應用服務 領域服務 ddd理論學習系列...