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

相关文章
相关标签/搜索