閱讀目錄:
- 1.背景介紹
- 2.過程式程式碼的真正困境
- 3.工作單元模式的簡單示例
- 4.總結
1.背景介紹
一直都在談論物件導向開發,但是開發企業應用系統時,使用物件導向開發最大的問題就是在於,多個物件之間的互操作需要涉及資料庫操作。兩個業務邏輯物件彼此之間需要互相呼叫,如果之間的互相操作是在一個業務事務範圍內的,很容易完成,但是如果本次業務邏輯操作涉及到多個業務物件一起協作完成時問題就來了。
在以往,我們使用過程式的程式碼(事務指令碼模式),將所有與本次業務事務範圍內相關的所有邏輯都寫在一個大的程式碼中,就算你適當的提取重複程式碼,效果也不大,因為你永遠都擺脫不了誇多個物件互相操作的困境。如何確認你是否在這個困境中,你只要看你的所有事務操作的入口都只有一個業務方法。比如當你新增一個訂單的時候,你同時將訂單跟隨的商品都一起在“新增訂單”的方法中處理的,而不是在另外一個“新增訂單商品”的方法中,這兩個方法位於不同的表模組類中。
本章將介紹一個模式,此模式專門用來在開發企業應用系統時,協調多個業務物件在一個業務事務範圍內,保證一個完整的事務。
2.過程式程式碼的困境
其實開發應用系統與開發某個框架或者元件之間的最大區別就是需要考慮資料的持久化,而持久化的邏輯也是和業務邏輯息息相關的,某個方法的最後動作就有可能是新增一行資料或者更新一個欄位。而非應用系統的程式碼往往在最後的時候才去統一重新整理最終的持久化檔案,而且此類程式很少存在事務性資料操作。就算有,使用記憶體事務處理也是比較簡單的,不需要考慮那麼多的服務端的事情。
我之前也寫過很多元件、框架,雖然談不上什麼複雜的東西,但是給我的經驗和感悟就是,如何將其細緻的設計粒度用在企業應用系統中,如何進行復雜而細緻的OO設計開發。其實,如果我們不能夠打破過程式程式碼的格局,那麼看再多的OO知識也是心有餘而力不足,反而會讓你產生很多負面的情緒(因為我有過這個經歷)。
其實我們還是缺少正確的方法而已,本文中UnitOfWork模式將幫助我們走出過程式的業務邏輯,走向起碼的物件導向開發。有了UnitOfWork你可以隨意使用Table module 、Activa Record、Domin Driven 模式,而且你可以根據自己的專案需要將其在大的佈局上進行SOA劃分(CQRS),讓各個模式在各自適合的場景中發揮極致。
3.工作單元模式的簡單示例
這裡我們依然使用簡單的訂單購物業務作為示例來講,畢竟大家都懂得這部分的的業務概念。本例項業務層使用Active Record模式。
1 namespace OrderManager.Business 2 { 3 using System.Collections.Generic; 4 5 public partial class Order 6 { 7 public long OId { get; set; } 8 9 public List<OrderProducts> Products { get; set; } 10 } 11 }
Order活動記錄物件的欄位部分。
1 namespace OrderManager.Business 2 { 3 public partial class Order 4 { 5 public bool CheckOrder() 6 { 7 //執行部分業務驗證工作 8 if (this.OId <= 0) return false; 9 10 return true; 11 } 12 } 13 }
Order活動記錄物件主體,純粹為了演示而用,包含了一個簡單的判斷業務邏輯。
1 namespace OrderManager.Business 2 { 3 public partial class OrderProducts 4 { 5 public long OrderId { get; set; } 6 7 public long PId { get; set; } 8 9 public float Price { get; set; } 10 } 11 }
訂單商品部分欄位。
1 namespace OrderManager.Business 2 { 3 public partial class OrderProducts 4 { 5 public bool CheckProducts() 6 { 7 //執行部分業務驗證工作 8 if (this.OrderId <= 0) return false; 9 10 return true; 11 } 12 } 13 }
每一個商品都包含了自己的邏輯驗證。
我們接著看一下應用層入口方法是如何協調兩個活動記錄物件之間的業務操作和資料儲存的。
1 namespace OrderManager 2 { 3 using OrderManager.Business; 4 using OrderManager.DataSource; 5 6 public class OrderManagerController : ControllerBase 7 { 8 public bool AddOrder(Order order) 9 { 10 using (UnitOfWork unitOfWork = new UnitOfWork()) 11 { 12 order.CheckOrder();//執行業務檢查 13 14 order.Products.ForEach(item => 15 { 16 item.CheckProducts();//執行每個活動記錄物件的業務檢查,這裡也可以使用表模組來處理。 17 }); 18 19 OrderGateway orderGateway = new OrderGateway(unitOfWork); 20 var orderDbResult = orderGateway.AddOrder(order);//第一個資料庫表操作 21 22 OrderProductsGateway productGateway = new OrderProductsGateway(unitOfWork); 23 var productDbResult = productGateway.AddOrderProducts(order.Products);//第二個資料庫表操作 24 25 if (orderDbResult && productDbResult) 26 { 27 if (unitOfWork.Commit()) 28 { 29 this.SendOrderIntegrationMssage(order);//傳送成功整合訂單訊息 30 31 return true; 32 } 33 34 this.PushOrderProcessQueue(order);//將本次訂單傳送到處理佇列中 35 return false; 36 } 37 38 this.LogBusinessException(order);//記錄一個業務處理異常LOG,以備排查問題。 39 return false; 40 } 41 } 42 } 43 }
為了簡單演示示例,我直接使用例項化的方式來構造資料訪問物件,實際使用時可以使用IOC工具來動態注入。
我們接著看一下資料層程式碼,資料層我使用表入口模式。
1 namespace OrderManager.DataSource 2 { 3 public abstract class GatewayBase 4 { 5 protected UnitOfWork UnitOfWork { get; private set; } 6 7 public GatewayBase(UnitOfWork unit) 8 { 9 this.UnitOfWork = unit; 10 } 11 12 public bool Commit() 13 { 14 return this.UnitOfWork.Commit(); 15 } 16 17 public void Rollback() 18 { 19 this.UnitOfWork.Rollback(); 20 } 21 } 22 }
這是一個表入口基類。
1 namespace OrderManager.DataSource 2 { 3 using OrderManager.Business; 4 5 public class OrderGateway : GatewayBase 6 { 7 public OrderGateway(UnitOfWork unit) : base(unit) { } 8 9 public bool AddOrder(Order order) 10 { 11 //這裡可以使用你所熟悉的拼接SQL的方式直接運算元據庫,而不需要ORM。 12 return true; 13 } 14 } 15 }
1 namespace OrderManager.DataSource 2 { 3 using OrderManager.Business; 4 using System.Collections.Generic; 5 6 public class OrderProductsGateway : GatewayBase 7 { 8 public OrderProductsGateway(UnitOfWork unit) : base(unit) { } 9 10 public bool AddOrderProducts(List<OrderProducts> products) 11 { 12 //這裡可以使用你所熟悉的拼接SQL的方式直接運算元據庫,而不需要ORM。 13 return true; 14 } 15 } 16 }
這是兩個表入口物件,其實這部分程式碼是大家都比較熟悉的,所以我這裡省略了,你可以直接拼接SQL語句來插入資料庫。
1 namespace OrderManager.DataSource 2 { 3 using System; 4 5 public class UnitOfWork : IDisposable 6 { 7 public void Dispose() 8 { 9 throw new NotImplementedException(); 10 } 11 12 public bool Commit() 13 { 14 return true; 15 } 16 17 public void Rollback() 18 { 19 // 20 } 21 } 22 }
UnitOfWrok物件其實就是對資料庫物件的System.Data.Common.DbConnection物件的封裝,這裡你可以使用你熟悉的方式來構造這個資料庫連線物件和開啟事務。
其實值得我們去欣賞的是應用控制器中的程式碼,在這裡很協調的處理各個邏輯,最後記錄下一些必要的日誌和傳送一些整合訊息。你是不是發現你完全可以不使用DDD也可以處理部分業務系統了。
4.總結
活動記錄模式+表入口模式+工作單元模式,其實我覺得可以很好的處理中小型業務邏輯,隨著現在SOA化架構,很少再有多大的專案在一個解決方案裡面。
最後還是那句話,提供一個參考資料,如果有興趣可以進一步交流具體的設計,由於時間關係文章就到這裡了,謝謝大家。