Unit Of Work的设计
在DDD开发过程中,一个良好的Uow设计必不可少,我心目中的Uow设计应该具备以下几点: 1、有着良好的抽象,有着恰如其分的命名; 2、能够应付不同的组件,比如你的系统中可能会存在EfUnitOfWork、RedisUnitOfWork; 3、易于使用,不用开发者显示调用。Uow在一个用户请求开始到结束能够自动包裹在业务逻辑外边; 在阅读了Abp的源码后我感觉Abp中的Uow正好符合我这几点要求,但是其实现稍有点复杂,例如在EfUnitOfWork中加入了DynamicFilters,EfUnitOfWork支持多DbContext,这些复杂性导致EfUnitOfWork类变得有点臃肿。所以我在实例代码中移除了这些设计。 @H_403_10@一、业务抽象一个使用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的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; } }@H_403_10@三、通过Castle实现Aop,将Uow包裹ApplicationService层 定义UnitOfWorkInterceptor,该拦截器表现为要对现有的一个方法包裹UnitOfWork实现。 private void PerformUow(IInvocation invocation,UnitOfWorkOptions options) { using (var uow = _unitOfWorkManager.Begin(options)) { invocation.Proceed(); uow.Complete(); } }@H_403_10@四、缺陷 该方案是一个很优秀的UnitOfWork设计,不过当我们在使用DDD模型时如果引申出了领域事件,该方案则不够理想。当领域模型未能够正确持久化时则不应该发布领域事件。针对这一要求我有两个想法: 1、将发布领域事件注册在UnitOfWorkManager的Completed阶段,确保EfUnitOfWork正确执行后再发布领域事件; 2、抽象出EfUnitOfWorkParticipant、IServiceBusParticipant。在Commit阶段分别Commit这两个participant,有一个失败则Rollback所有的participant。 ? 具体实现请参考:https://git.oschina.net/richieyangs/BookLibrary.git (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |