在DDD開發過程當中,一個良好的Uow設計必不可少,我心目中的Uow設計應該具有如下幾點:html
一、有着良好的抽象,有着恰如其分的命名;git
二、可以應付不一樣的組件,好比你的系統中可能會存在EfUnitOfWork、RedisUnitOfWork;github
三、易於使用,不用開發者顯示調用。Uow在一個用戶請求開始到結束可以自動包裹在業務邏輯外邊;ide
在閱讀了Abp的源碼後我感受Abp中的Uow正好符合我這幾點要求,可是其實現稍有點複雜,例如在EfUnitOfWork中加入了DynamicFilters,EfUnitOfWork支持多DbContext,這些複雜性致使EfUnitOfWork類變得有點臃腫。因此我在實例代碼中移除了這些設計。.net
1、業務抽象設計
一個使用Uow的典型代碼片斷以下:orm
using (var uow = UowManager.Begin()) { //....logic uow.Commit(); }
從這段代碼中咱們基本能夠分析出下面的Uow抽象。htm
一、IUnitOfWorkManagerblog
從上面的代碼片斷能夠看出,此Manager應該具備一個Begin()方法,返回IDisposable類型。接口
二、IUnitOfWorkCompleteHandle
IUnitOfWorkManager的Begin()方法返回的IDisposable類型應該具備Commit()的能力,該抽象用於對IUnitOfWork的提交和釋放資源。
三、IUnitOfWork
從這個代碼片斷中咱們並無看到IUnitOfWork這個抽象,緣由在於IUnitOfWorkManager隱藏了具體的IUnitOfWork,IUnitOfWorkManager.Begin()實現中其實是具體的IUnitOfWork.Begin()調用。
四、ICurrentUnitOfWorkProvider
針對用戶在一次上下文中的請求,具備惟一的一個IUnitOfWork,所以能夠在任意時刻經過ICurrentUnitOfWorkProvider來讀取當前上下文的IUnitOfWork。
經過接口命名描述就能理清整個Uow設計思路:
2、EfUnitOfWork
EfUnitOfWork的Begin體如今對TransactionScope的調用、Commit體如今對dbContext.SaveChanges()的調用
public void Begin(UnitOfWorkOptions options) { _transactionScope = new TransactionScope( options.TransactionScopeOption.GetValueOrDefault(_defaultUnitOfWorkOptions.TransactionScopeOption), new TransactionOptions() { IsolationLevel = options.IsolationLevel.GetValueOrDefault(_defaultUnitOfWorkOptions.IsolationLevel), Timeout = options.Timeout.GetValueOrDefault(_defaultUnitOfWorkOptions.Timeout) }); }
public void Complete() { try { _dbContext.SaveChanges(); _transactionScope?.Complete(); _completed?.Invoke(); } catch { _failed?.Invoke(); throw; } }
3、經過Castle實現Aop,將Uow包裹ApplicationService層
定義UnitOfWorkInterceptor,該攔截器表現爲要對現有的一個方法包裹UnitOfWork實現。
private void PerformUow(IInvocation invocation, UnitOfWorkOptions options) { using (var uow = _unitOfWorkManager.Begin(options)) { invocation.Proceed(); uow.Complete(); } }
4、缺陷
該方案是一個很優秀的UnitOfWork設計,不過當咱們在使用DDD模型時若是引伸出了領域事件,該方案則不夠理想。當領域模型未可以正確持久化時則不該該發佈領域事件。針對這一要求我有兩個想法:
一、將發佈領域事件註冊在UnitOfWorkManager的Completed階段,確保EfUnitOfWork正確執行後再發布領域事件;
二、抽象出EfUnitOfWorkParticipant、IServiceBusParticipant。在Commit階段分別Commit這兩個participant,有一個失敗則Rollback全部的participant。