.NET應用架構設計—適當使用活動記錄模式代替領域模型模式

王清培發表於2014-08-30

閱讀目錄:

  • 1.背景介紹
  • 2.簡單介紹領域模型模式、活動記錄模式
  • 3.活動記錄模式的簡單示例及要點
  • 4.總結

1.背景介紹

對軟體開發方法論有興趣的博友應該發現最近“領域驅動設計”慢慢的被人發現被人實踐起來,園子裡也慢慢有了DDD的學習氣氛和寶貴實戰經驗的分享。其實之前我也痴迷於DDD,為什麼會痴迷於它並不是因為它是所謂的新技術,也不是因為各種對它的炒作,而是我覺得我找到了能解放我們進行企業業務系統開發的方法論。

DDD可以很好的指導我們開發可靠的軟體系統,尤其是現在的企業業務複雜多變的情況下,使用DDD可以很好的隨著業務變化不斷的重構現有的領域模型,最為重要的是我覺得DDD是能夠很好的實施敏捷價值觀的軟體開發方法論。

如果你想重構、測試你所寫的業務程式碼,少不了對程式碼進行適當的羅動,如果沒有一個好的結構讓你存放你所提取出來的程式碼是比較無奈的。包括現在敏捷軟體開發方法論中最重要的TDD方法論更加的依賴程式碼的結構是否能夠允許進行重構,如果你的結構是很死板的,扁平化的其實很難實施TDD,你會發現你所抽象出來的概念無恥容納。

所以我認為DDD是為了解決上述這些問題的一個好的方法,當然前提是你自己實施過之後才有資格去評判它的優劣,而且是客觀公正的。

實施DDD是很費時費力的工程,沒有傳統的開發方法那麼簡單快捷,而且對開發人員的整體要求有了一個新的標準,所以本篇文章將介紹一個可以用來替代DDD模式的另外一個比較好的企業模式“活動記錄模式”。

2.簡單介紹領域模型模式、活動記錄模式

領域模型模式其實就是領域驅動設計,兩個是一個意思。有興趣的朋友可以進一步學習領域驅動設計,我認為DDD對於一名企業應用開發人員來說是必不可少的一門設計思想,就好比設計模式一樣,它也有著一套模式,用來指導我們進行相關業務場景的設計。

領域模型模式也稱領域驅動設計,對業務模型進行等價的物件導向建模,無需太多考慮資料儲存的技術細節,但是並不是說完全不考慮如何儲存,如果誰告訴你說完全不需要考慮儲存那是錯誤的,因為你要考慮這個領域模型最終是要如何持久化的,以免你將領域模型建立成一個巨大的蜘蛛網。說不需要考慮領域模型如何持久化其實是說你目前只需要把握領域模型建立,不去完成持久化設計細節而已,但是這兩個工作往往是互相考慮的。

難道一個不懂得如何儲存關係資料的人能夠建立出能滿足程式設計師很好的開發的模型嗎。

活動記錄模式是最靠近DDD的模式,它將資料庫中的表中的一行作為自己的物件化欄位,然後圍繞著這些欄位展開的業務邏輯,將資料欄位與業務邏輯都封裝在一個例項物件中。

活動記錄模式與表模組模式不同的是,表模組模式是一個物件對應著一個資料庫中的表,而活動記錄模式是一個物件對應著一個行記錄,所以稱為活動記錄模式。

此模式最大好處是可以基本上滿足業務不是很複雜的情景下,我倒覺得活動記錄模式在現在的面向SOA架構下能夠更適合企業的業務系統開發團隊。使用領域驅動太過於複雜,不使用又會面臨著業務快速變化的困境,所以活動記錄模式可以考慮試試。

3.活動記錄模式的簡單示例及要點

我們來看一個簡單的示例,瞭解活動記錄模式的開發及要點。

活動記錄模式是使用與資料庫中的表結構一直的方式使用類的,也就是說表中的列就是類的欄位,當然也可以在處理業務邏輯時的輔助欄位,儘量不包含多餘的欄位,這樣可以有效保證乾淨的活動記錄。

 1 namespace Business.RecordModels.OrderModel
 2 {
 3     using System;
 4     using System.Collections.Generic; 
 5 
 6     /// <summary>
 7     /// Order table fields.
 8     /// </summary>
 9     public partial class OrderFields
10     {
11         /// <summary>
12         /// Order id.
13         /// </summary>
14         public long OId { get; set; } 
15 
16         /// <summary>
17         /// Order name customer declare.
18         /// </summary>
19         public string OName { get; set; } 
20 
21         /// <summary>
22         /// Product ids.
23         /// </summary>
24         public List<long> PIds { get; set; } 
25 
26         /// <summary>
27         /// Order sum price.
28         /// </summary>
29         public float Price { get; set; } 
30 
31         /// <summary>
32         /// Order distribute status.
33         /// </summary>
34         public int DistributeStatus { get; set; } 
35 
36         /// <summary>
37         /// Order payment status.
38         /// </summary>
39         public int PaymentStatus { get; set; } 
40 
41         /// <summary>
42         /// Order signer.
43         /// </summary>
44         public string Signer { get; set; } 
45 
46         /// <summary>
47         /// Submit datetime.
48         /// </summary>
49         public DateTime SubmitDt { get; set; }
50     }
51 } 

定義一個Order活動記錄的欄位類,也可以直接將該欄位定義在Order類中,但是一般我喜歡獨立出來,因為欄位一旦多了看起來實在很累。如果你想將欄位直接定義在Order類中我建議你使用部分類來處理,這樣比較乾淨。

欄位中的public List<long> PIds { get; set; } 商品ID集合欄位是有意聚合到該欄位類中的,因為不管是業務處理還是資料持久化都是需要相關連的業務欄位的。  (活動記錄模式不要求你很死板的一個表一個記錄例項,只要你使用你自己的方式能夠讓程式碼結構看上去很自然就是很恰當的。)

雖然你直接使用了int型別來描述業務欄位,不過你可以使用其他方式讓來該欄位在業務處理中不直接使用語言型別而是業務概念。

 1 namespace Business.RecordModels.OrderModel
 2 {
 3     /// <summary>
 4     /// Distribute status const.
 5     /// </summary>
 6     public class DistributeStatusConst
 7     {
 8         public const int NoDistribute = 1; 
 9 
10         public const int BeginDistribute = 2; 
11 
12         public const int EndDistribute = 3;
13     }
14 } 

配送狀態常量類,定義明確的業務概念的值。

 1 namespace Business.RecordModels.OrderModel
 2 {
 3     /// <summary>
 4     /// Payment status const.
 5     /// </summary>
 6     public class PaymentStatusConst
 7     {
 8         /// <summary>
 9         /// No payment.
10         /// </summary>
11         public const int NoPayment = 1; 
12 
13         /// <summary>
14         /// End payment.
15         /// </summary>
16         public const int EndPayment = 1;
17     }
18 } 

配送狀態常量類,定義明確的業務概念的值。

對活動記錄的建立我建議是用工廠來處理,畢竟這裡麵包含了很多業務邏輯在裡面的。

 1 namespace Business.RecordModels.OrderModel
 2 {
 3     using Common; 
 4 
 5     /// <summary>
 6     /// Order class factory.
 7     /// </summary>
 8     public class OrderFactory
 9     {
10         /// <summary>
11         /// Create a order instance.
12         /// </summary>
13         /// <param name="fields">Order fiels instance.</param>
14         /// <returns>Order instance.</returns>
15         public static Order CreateOrder(OrderFields fields)
16         {
17             if (fields.IsNull() || fields.OName.IsNullOrEmpty()
18                 || fields.PIds.IsNullOrEmpty() || fields.Price.IsLessThanOrEqual0()) return null; 
19 
20             fields.DistributeStatus = DistributeStatusConst.NoDistribute;//No distribute.
21             fields.PaymentStatus = PaymentStatusConst.NoPayment;//No payment. 
22 
23             return new Order(fields);
24         }
25     }
26 }

活動記錄模式不等於沒有DDD那麼好,其實在業務相對不是非常複雜的情況下,活動記錄模式還是相當不錯的,簡單快捷,對一些原子型別的欄位處理使用常量就很不錯。

這裡我們使用DistributeStatusConst.NoDistribute、PaymentStatusConst.NoPayment 常量欄位來明確的傳達業務概念,而不是非要用列舉,經驗告訴我有時候列舉沒有常量方便。

活動記錄物件中包含了該記錄所表達的業務邏輯,這裡的Order將包含該表所表達的業務邏輯處理。

 1 namespace Business.RecordModels.OrderModel
 2 {
 3     using System; 
 4 
 5     /// <summary>
 6     /// Order table record
 7     /// </summary>
 8     public partial class Order
 9     {
10         /// <summary>
11         /// Order table columns.
12         /// </summary>
13         private OrderFields fields { get; set; } 
14 
15         /// <summary>
16         /// New a order instance only internal use.
17         /// </summary>
18         /// <param name="fields">Order fields.</param>
19         internal Order(OrderFields fields)
20         {
21             this.fields = fields;
22         } 
23 
24         /// <summary>
25         /// Calculate order expiration time.
26         /// </summary>
27         /// <returns>DateTime</returns>
28         public DateTime CalculateExpirationTime()
29         {
30             //Here you can use strategy.
31             if (this.fields.PaymentStatus == PaymentStatusConst.NoPayment)//No payment logic
32             {
33                 return this.fields.SubmitDt.AddDays(1);
34             }
35             else if (this.fields.DistributeStatus == DistributeStatusConst.NoDistribute)//No payment logic
36             {
37                 return this.fields.SubmitDt.AddDays(30);
38             } 
39 
40             return this.fields.SubmitDt.AddDays(3);
41         }
42     }
43 } 

這裡我有一個簡單的計算當前訂單到期時間的方法(CalculateExpirationTime),在方法內部有一些業務邏輯,而該業務邏輯和當前例項一起存在。

通過使用活動記錄模式可以很好的將欄位與業務方法有效的集合起來,這樣會使得業務邏輯處理比較有條理性,也便於測試和重構。

這裡需要強調的是活動記錄模式是業務層和資料層共用的模式,當時這裡我們所講的是面向業務層的,也就是說你資料層可以使用任何方式來和活動記錄模式整合,現在比較流行ORM了,如果你對效能有要求你可以使用手工處理,建議使用表入口模式來結合,因為資料層沒有什麼邏輯,如果你的資料層有相關的邏輯我像也不會出現最後的資料來源上,而是應該在資料適配層上處理掉,如:快取、填補欄位等。

4.總結

很難在一篇文章中說明所有問題,活動記錄模式如果是用在讀寫分離大的架構中的寫端時必須需要“工作單元”模式來協調多“記錄”之間的事務性。但是如果你在查詢端使用活動記錄模式,那麼大部分情況下是不需要事務性的,當然查詢端我覺得使用事物指令碼模式比較直觀點,因為業務邏輯也不會有多少。

還是那句話,本篇文章只是分享點自己學習過程中和工作過程總結的經驗,僅供參考。其實企業應用架構是一個看似簡單其實很複雜的方向,希望與各位一起學習一同進步,謝謝。

 

相關文章