zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    spring boot , spring security 安全的认证
    C# 插件编写
    linux diff命令详解 和 patch命令
    nginx location 匹配规则
    LVM扩容报错以及 xfs_growfs 和 resize2fs 区别
    shell ps命令 以及 ps命令 进程时间 格式转换
    shell ls 命令
    Ajax类库需要注意的问题
    JS中的基本运动逻辑思想总结
    Ajax读取文件时出现的缓存问题
  • 原文地址:https://www.cnblogs.com/richieyang/p/5593197.html
Copyright © 2011-2022 走看看