Unit Of Work的設計

在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。

 

具體實現請參考:https://git.oschina.net/richieyangs/BookLibrary.git

相關文章
相關標籤/搜索