• Unit Of Work的设计


    在DDD开发过程中,一个良好的Uow设计必不可少,我心目中的Uow设计应该具备以下几点:

    1、有着良好的抽象,有着恰如其分的命名;

    2、能够应付不同的组件,比如你的系统中可能会存在EfUnitOfWork、RedisUnitOfWork;

    3、易于使用,不用开发者显示调用。Uow在一个用户请求开始到结束能够自动包裹在业务逻辑外边;

    在阅读了Abp的源码后我感觉Abp中的Uow正好符合我这几点要求,但是其实现稍有点复杂,例如在EfUnitOfWork中加入了DynamicFilters,EfUnitOfWork支持多DbContext,这些复杂性导致EfUnitOfWork类变得有点臃肿。所以我在实例代码中移除了这些设计。

    一、业务抽象

    一个使用Uow的典型代码片段如下:

                using (var uow = UowManager.Begin())
                {
                    //....logic
    
                    uow.Commit();
                }
    

    从这段代码中我们基本可以分析出下面的Uow抽象。

    1、IUnitOfWorkManager

    从上面的代码片段可以看出,此Manager应该具有一个Begin()方法,返回IDisposable类型。

    2、IUnitOfWorkCompleteHandle

    IUnitOfWorkManager的Begin()方法返回的IDisposable类型应该具有Commit()的能力,该抽象用于对IUnitOfWork的提交和释放资源。

    3、IUnitOfWork

    从这个代码片段中我们并没有看到IUnitOfWork这个抽象,原因在于IUnitOfWorkManager隐藏了具体的IUnitOfWork,IUnitOfWorkManager.Begin()实现中实际上是具体的IUnitOfWork.Begin()调用。

    4、ICurrentUnitOfWorkProvider

    针对用户在一次上下文中的请求,具有唯一的一个IUnitOfWork,因此可以在任意时刻通过ICurrentUnitOfWorkProvider来读取当前上下文的IUnitOfWork。

    通过接口命名描述就能理清整个Uow设计思路:

    二、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;
                }
               
            }
    

    三、通过Castle实现Aop,将Uow包裹ApplicationService层

    定义UnitOfWorkInterceptor,该拦截器表现为要对现有的一个方法包裹UnitOfWork实现。

            private void PerformUow(IInvocation invocation, UnitOfWorkOptions options)
            {
                using (var uow = _unitOfWorkManager.Begin(options))
                {
                    invocation.Proceed();
                    uow.Complete();
                }
            }

    四、缺陷

    该方案是一个很优秀的UnitOfWork设计,不过当我们在使用DDD模型时如果引申出了领域事件,该方案则不够理想。当领域模型未能够正确持久化时则不应该发布领域事件。针对这一要求我有两个想法:

    1、将发布领域事件注册在UnitOfWorkManager的Completed阶段,确保EfUnitOfWork正确执行后再发布领域事件;

    2、抽象出EfUnitOfWorkParticipant、IServiceBusParticipant。在Commit阶段分别Commit这两个participant,有一个失败则Rollback所有的participant。

    具体实现请参考:https://git.oschina.net/richieyangs/BookLibrary.git

  • 相关阅读:
    布局及视图(三)
    笔试中的编程题2
    布局及视图(四)
    SoftReference,WeakReference&WeakHashMap
    Android自用 监测网络是否可用
    Android自用 加载png图片时出错!
    Android访问权限大全
    笔试中的编程题3
    如何全面的把握一个系统的异常处理
    从程序的控制逻辑看线程的三种应用模式
  • 原文地址:https://www.cnblogs.com/richieyang/p/5593197.html
Copyright © 2020-2023  润新知