程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> EntityFramework之領域驅動設計實踐(六):模型對象的生命周期 - 工廠

EntityFramework之領域驅動設計實踐(六):模型對象的生命周期 - 工廠

編輯:關於.NET

首先應該認識到,是對象就有生命周期。這一點無論在面向對象語言還是在領域驅動設計中都適用。在領域驅動設計中,模型對象生命周期可以簡要地用下圖表示:

通過上圖可以看到,對象通過工廠從無到有創建,創建後處於活動狀態,此時可以參與領域層的業務處理;對象通過倉儲實現持久化(也就是我們常說的“保存”)和重建(也就是我們常說的“讀取”)。內存中的對象通過析構而消亡,處於持久化狀態的對象則通過倉儲進行撤銷(也就是我們常說的“刪除”)。整個狀態轉換過程非常清晰。

現在引出了管理模型對象生命周期的兩種角色:工廠和倉儲。同時也需要注意的是,工廠和倉儲的操作都是基於聚合根(Aggregate Root)的,而不僅僅是針對實體的。關於倉儲,內容會比較多,我在下一節單獨講述。在本節介紹一下工廠在.NET實體框架(EntityFramework)中的實現。

在打開了.NET實體框架自動生成的Entity Data Model Source Code文件後,我們發現,.NET實體框架為每一個實體添加了一個工廠方法,該方法包含了一系列原始數據類型和值類型的參數。比如,我們案例中的 Customer實體就有如下的代碼:

Customer Factory

/// <summary>
/// Create a new Customer object.
/// </summary>
/// <param name="id">Initial value of the Id property.</param>
/// <param name="name">Initial value of the Name property.</param>
/// <param name="billingAddress">Initial value of the BillingAddress property.</param>
/// <param name="deliveryAddress">Initial value of the DeliveryAddress property.</param>
/// <param name="loginName">Initial value of the LoginName property.</param>
/// <param name="loginPassword">Initial value of the LoginPassword property.</param>
/// <param name="dayOfBirth">Initial value of the DayOfBirth property.</param>
public static Customer CreateCustomer(global::System.Int32 id, Name name, Address billingAddress,
                        Address deliveryAddress, global::System.String loginName,
                        global::System.String loginPassword, global::System.DateTime dayOfBirth)
{
   Customer customer = new Customer();
   customer.Id = id;
   customer.Name = StructuralObject.VerifyComplexObjectIsNotNull(name, "Name");
   customer.BillingAddress = StructuralObject.VerifyComplexObjectIsNotNull(billingAddress, "BillingAddress");
   customer.DeliveryAddress = StructuralObject.VerifyComplexObjectIsNotNull(deliveryAddress, "DeliveryAddress");
   customer.LoginName = loginName;
   customer.LoginPassword = loginPassword;
   customer.DayOfBirth = dayOfBirth;
   return 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實體框架,但又不被實體框架制約的倉儲的實現方式。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved