閱讀目錄:
- 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化架構,很少再有多大的項目在一個解決方案里面。
最后還是那句話,提供一個參考資料,如果有興趣可以進一步交流具體的設計,由於時間關系文章就到這里了,謝謝大家。