模型對象的生命週期 - 工廠
首先應該認識到,是對象就有生命週期。這一點無論在面嚮對象語言還是在領域驅動設計中都適用。在領域驅動設計中,模型對象生命週期可以簡要地用下圖表示:
通過上圖可以看到,對象通過工廠從無到有創建,創建後處於活動狀態,此時可以參與領域層的業務處理;對象通過倉儲實現持久化(也就是我們常說的“保存”)和重建(也就是我們常說的“讀取”)。內存中的對象通過析構而消亡,處於持久化狀態的對象則通過倉儲進行撤銷(也就是我們常說的“刪除”)。整個狀態轉換過程非常清晰。
現在引出了管理模型對象生命週期的兩種角色:工廠和倉儲。同時也需要注意的是,工廠和倉儲的操作都是基於聚合根(Aggregate Root)的,而不僅僅是針對實體的。關於倉儲,內容會比較多,我在下一節單獨講述。在本節介紹一下工廠在.NET實體框架(EntityFramework)中的實現。
在打開了.NET實體框架自動生成的Entity Data Model Source Code文件後,我們發現,.NET實體框架爲每一個實體添加了一個工廠方法,該方法包含了一系列原始數據類型和值類型的參數。比如,我們案例中的 Customer實體就有如下的代碼:
那麼在創建一個Customer實體的時候,就可以使用 Customer.CreateCustomer工廠方法。看來.NET實體框架已經離領域驅動設計的思想比較接近了,下面有幾點需要說明:
- 使用該工廠方法創建Customer實體時,需要給第一個參數 “global::System.Int32 id”賦值,而實際上這個ID值是用在持久化機制上的,在實體對象被創建的時候,這個ID值不應該由開發人員指定。因此,在這裏要開發人員強行指定一個 id值就顯得多餘。事實上,.NET實體框架中的每個實體都是繼承於EntityObject類,而該類中有個EntityKey的屬性,是被用作實體的 Key的,因此我們這裏的ID值肯定是由持久化機制進行維護的。從這裏也可以看出,領域驅動設計中的實體會有兩個標識符:一個是基於業務架構的,另一個是基於技術架構的。拿銷售訂單打比方,我們從界面上看到的更多是類似“SO0029473858” 這樣的標識符,而不是一個整數或者GUID
-
該工廠方法能夠創建一個Customer實體,爲實體的各個成員屬性賦值,並連帶創建與該實體相關的值對象,聚合成員(比如 Customer的CreditCards)是在使用的時候進行創建並填充的,這樣做既符合“對象創建應該基於聚合”的思想,又能提高系統性能。比如,下面的單體測試用來檢測使用工廠創建的Customer對象,其CreditCards屬性是否爲null(如果爲null,則證明聚合根並沒有合理地維護聚合的完整性):
-
.NET實體框架僅僅爲每個實體提供了一個最爲簡單的工廠方法。“工廠”的概念,在領域驅動設計中具有如下的最佳實踐:
- 工廠可以隱藏對象創建的細節,因爲對象的創建不屬於業務領域需要考慮的問題
- 工廠用來創建整個聚合,從而維護聚合所代表的領域含義
- 可以在聚合根中添加工廠方法,也可以使用工廠類。也就是說,可以創建一個CustomerFactory的類,在其中定義 CreateCustomer方法。具體是選用工廠方法還是工廠類,應該根據需求而定
- 當需要對被創建的實體傳入參數時,應該儘可能地減小耦合性,比如可以使用抽象類或者接口作爲參數類型
到這裏你會發現,工廠和倉儲好像有這一種聯繫,即它們都能夠創建對象,而區別在於,工廠是對象從無到有的創建,倉儲則更偏向於“重建”。倉儲要比工廠更爲複雜,因爲倉儲需要跟持久化機制這一技術架構打交道。在接下來的文章中,我會介紹一種基於.NET實體框架,但又不被實體框架制約的倉儲的實現方式。
-----【以下爲原文網友評論及回覆信息】----- |
[ 2009-12-31 11:48:00 | By: xiaos(遊客) ] 感覺repository 職責應該是關注get和load,而factory則是create。
以下爲blog主人的回覆: |
[ 2010-2-9 21:34:00 | By: haojie77 ]
我最近也在學習DDD, 你這篇文章中提到的"從這裏也可以看出,領域驅動設計中的實體會有兩個標識符:一個是基於業務架構的,另一個是基於技術架構的。" 我有兩個疑問.
以下爲blog主人的回覆: 出自:http://www.cnblogs.com/daxnet/archive/2010/07/07/1772615.html |