EntityFramework之領域驅動設計實踐(六)

來源:互聯網
上載者:User
文章目錄
  • 模型對象的生命週期 - 工廠
模型對象的生命週期 - 工廠

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

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

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

在開啟了.NETEntity Framework自動產生的Entity Data Model Source Code檔案後,我們發現,.NETEntity Framework為每一個實體添加了一個Factory 方法,該方法包含了一系列未經處理資料類型和實值型別的參數。比如,我們案例中的 Customer實體就有如下的代碼:

隱藏行號 複製代碼 ? Customer Factory
  1. /// <summary>
  2. /// Create a new Customer object.
  3. /// </summary>
  4. /// <param name="id">Initial value of the Id property.</param>
  5. /// <param name="name">Initial value of the Name property.</param>
  6. /// <param name="billingAddress">Initial value of the BillingAddress property.</param>
  7. /// <param name="deliveryAddress">Initial value of the DeliveryAddress property.</param>
  8. /// <param name="loginName">Initial value of the LoginName property.</param>
  9. /// <param name="loginPassword">Initial value of the LoginPassword property.</param>
  10. /// <param name="dayOfBirth">Initial value of the DayOfBirth property.</param>
  11. public static Customer CreateCustomer(global::System.Int32 id, Name name, Address billingAddress, 
  12.                                              Address deliveryAddress, global::System.String loginName, 
  13.                                              global::System.String loginPassword, global::System.DateTime dayOfBirth)
  14. {
  15.     Customer customer = new Customer();
  16.     customer.Id = id;
  17.     customer.Name = StructuralObject.VerifyComplexObjectIsNotNull(name, "Name");
  18.     customer.BillingAddress = StructuralObject.VerifyComplexObjectIsNotNull(billingAddress, "BillingAddress");
  19.     customer.DeliveryAddress = StructuralObject.VerifyComplexObjectIsNotNull(deliveryAddress, "DeliveryAddress");
  20.     customer.LoginName = loginName;
  21.     customer.LoginPassword = loginPassword;
  22.     customer.DayOfBirth = dayOfBirth;
  23.     return customer;
  24. }

 

那麼在建立一個Customer實體的時候,就可以使用 Customer.CreateCustomerFactory 方法。看來.NETEntity Framework已經離領域驅動設計的思想比較接近了,下面有幾點需要說明:

 

  • 使用該Factory 方法建立Customer實體時,需要給第一個參數 “global::System.Int32 id”賦值,而實際上這個ID值是用在持久化機制上的,在實體物件被建立的時候,這個ID值不應該由開發人員指定。因此,在這裡要開發人員強行指定一個 id值就顯得多餘。事實上,.NETEntity Framework中的每個實體都是繼承於EntityObject類,而該類中有個EntityKey的屬性,是被用作實體的 Key的,因此我們這裡的ID值肯定是由持久化機制進行維護的。從這裡也可以看出,領域驅動設計中的實體會有兩個標識符:一個是基於業務架構的,另一個是基於技術架構的。拿銷售訂單打比方,我們從介面上看到的更多是類似“SO0029473858” 這樣的標識符,而不是一個整數或者GUID
  • 該Factory 方法能夠建立一個Customer實體,為實體的各個成員屬性賦值,並連帶建立與該實體相關的值對象,彙總成員(比如 Customer的CreditCards)是在使用的時候進行建立並填充的,這樣做既符合“對象建立應該基於彙總”的思想,又能提高系統效能。比如,下面的單體測試用來檢測使用工廠建立的Customer對象,其CreditCards屬性是否為null(如果為null,則證明彙總根並沒有合理地維護彙總的完整性):
  • .NETEntity Framework僅僅為每個實體提供了一個最為簡單的Factory 方法。“工廠”的概念,在領域驅動設計中具有如下的最佳實務:
    • 工廠可以隱藏對象建立的細節,因為對象的建立不屬於業務領域需要考慮的問題
    • 工廠用來建立整個彙總,從而維護彙總所代表的領域含義
    • 可以在彙總根中添加Factory 方法,也可以使用工廠類。也就是說,可以建立一個CustomerFactory的類,在其中定義 CreateCustomer方法。具體是選用Factory 方法還是工廠類,應該根據需求而定
    • 當需要對被建立的實體傳入參數時,應該儘可能地減小耦合性,比如可以使用抽象類別或者介面作為參數類型

到這裡你會發現,工廠和倉儲好像有這一種聯絡,即它們都能夠建立對象,而區別在於,工廠是對象從無到有的建立,倉儲則更偏向於“重建”。倉儲要比工廠更為複雜,因為倉儲需要跟持久化機制這一技術架構打交道。在接下來的文章中,我會介紹一種基於.NETEntity Framework,但又不被Entity Framework制約的倉儲的實現方式。

 

 

-----【以下為原文網友評論及回複資訊】-----
 

Re:Entity Framework之領域驅動實踐(六)

[ 2009-12-31 11:48:00 | By: xiaos(遊客) ]

感覺repository 職責應該是關注get和load,而factory則是create。

以下為blog主人的回複:
不錯,文中我也提到了:“工廠是對象從無到有的建立,倉儲則更偏向於“重建””。Repository需要與技術架構打交道,而Factory則不需要。

Re:Entity Framework之領域驅動實踐(六)

[ 2010-2-9 21:34:00 | By: haojie77 ]

我最近也在學習DDD, 你這篇文章中提到的"從這裡也可以看出,領域驅動設計中的實體會有兩個標識符:一個是基於業務架構的,另一個是基於技術架構的。" 我有兩個疑問.
1. 我的感覺在domain中,Order的標識符就應該是諸如“SO0029473858”的字串. 而GUID只是為了在資料庫儲存而用到的(或者是EF必須要用到), 是否所謂的GUID根本不能算是領域概念中實體的標識符? 之前看到別人的文章說切忌不要把標識符當成資料庫主鍵, 一個entity是否可以有n個(n>=2)標識符?
2. 標識符和資料庫主鍵應該不是一一對應的關係,是嗎? 標識符往往映射到資料庫主鍵, 但標識符不一定就是資料庫主鍵. 我這樣的理解對嗎?
我剛開始學習DDD, 所以對一些概念不是很清晰,希望能從您這裡學習到更多關於DDD的東西,很期待你之後的(八,九,十...),謝謝!

以下為blog主人的回複:
是的,一定要將資料庫主鍵與實體鍵區分開來。原因很簡單,前者是技術架構的內容,而後者則是業務領域。在領域模型中,具有相同實體鍵的兩個對象可以認定為同一個實體。當然,完全可以把業務實體鍵用作資料庫主鍵,比如:資料庫裡完全可以用“SO0029473858”這樣的銷售訂單號作為 SalesTable的主鍵,但請注意了,如果是這樣的話,你必須在技術的角度產生這個索引值,然後在持久化實體之前將其賦給實體鍵。換句話說,當使用實體鍵用作資料庫的主鍵時,維護這個索引值的機制是你的系統本身,而不是資料庫。這可能會對效能造成一定的影響,《領域驅動設計》一書中也提到過這個問題。相關的經典案例就是Microsoft Dynamics AX中的Number Sequence機制,它是一個可以深度擴充的序號產生機制,在Dynamics AX中,諸如銷售訂單號等都是由這個機制產生並維護的。
“一個entity是否可以有n個標識符”?答案是否定的。只能有一個。標識符用來唯一標識一個獨立的實體。我上面所說的兩個值,其中一個是技術架構層面的,在領域模型中,一個實體只能有一個標識符。
不知我上面的回答是否能夠幫到你。PS:最近沒時間更新,不過我會爭取儘快更新。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.