首页 新闻 会员 周边

DDD模式求解惑。

0
悬赏园豆:50 [已解决问题] 解决于 2015-03-27 23:44

在这几年的工作历程中,我对传统的三层结构开发觉得各种别扭和充满了疑惑,最近开始研究DDD并且想进行实践。
在研究DDD的这段时间,有几个问题一直没有得到解决,期望高人解答。
1、在分布式的DDD模式中,UnitOfWork和Repository的生命周期应该如何管理,大多的例子中,都是介绍通过IOC对Repository的构造函数中进行UnitOfWork进行注入,但都没有介绍注入对象的生命周期,在Application层或是DomainService中实例(注入)的Repository对象难道都有一个独立的UnitOfWork吗,还是单例的?Repository的生命周期又应该是什么?
2、在跨Repository提交时应该如何工作?在基于一个数据库的情况下,只从其中一个Repository取出UnitOfWork进行SaveChanges,感觉很别扭。这个问题在网络上一直没有搜索到相关的介绍。
3、UnitOfWork被注入到Repository中,除了约定之外,有没有什么方式避免UnitOfWork中的SaveChanges暴露在Repository内部,在不同的人员进行编码时可能会出现有人在Repository中调用了SaveChanges,导致外部数据的完整性提交被破坏?

问题补充:

 以下是国外开源项目NLayerApp的部分代码。

1、在Repository的构造函数中被注入了UnitOfWork,使Repository拥有了DBContext功能的同时,也将DBContext中的Commit方法暴露在内部。

 1     public class BankAccountRepository
 2         :Repository<BankAccount>,IBankAccountRepository
 3     {
 4         #region Constructor
 5 
 6         /// <summary>
 7         /// Create a new instance
 8         /// </summary>
 9         /// <param name="unitOfWork">Associated unit of work</param>
10         public BankAccountRepository(MainBCUnitOfWork unitOfWork)
11             : base(unitOfWork)
12         {
13             
14         }
15 
16         #endregion
17 
18         #region Overrides
19 
20         /// <summary>
21         /// Get all bank accounts and the customer information
22         /// </summary>
23         /// <returns>Enumerable collection of bank accounts</returns>
24         public override IEnumerable<BankAccount> GetAll()
25         {
26             //Repository中会暴露UnitOfWork中的Commit方法 
27             //this.UnitOfWork.Commit();
28 
29             var currentUnitOfWork = this.UnitOfWork as MainBCUnitOfWork;
30 
31             var set = currentUnitOfWork.CreateSet<BankAccount>();
32 
33             return set.Include(ba => ba.Customer)
34                       .AsEnumerable();
35             
36         }
37         #endregion

 2、在ApplicationService中,直接通过Repository中的UnitOfWork进行Commit。

假设多个Repository共享一个UnitOfWork实例,那么在跨Repository工作后,其实只需要其中一个进行Commit提交即可,虽然没什么问题,但是感觉很别扭。

 1         void SaveBankAccount(BankAccount bankAccount)
 2         {
 3             //validate bank account
 4             var validator = EntityValidatorFactory.CreateValidator();
 5 
 6             if (validator.IsValid<BankAccount>(bankAccount)) // save entity
 7             {
 8                 _bankAccountRepository.Add(bankAccount);
           //假设此处同时处理多个Repository
           //_bankAccountRepository.Add(bankAccount);
9 _bankAccountRepository.UnitOfWork.Commit(); 10 } 11 else //throw validation errors 12 throw new ApplicationValidationErrorsException(validator.GetInvalidMessages(bankAccount)); 13 14 15 //IUnitOfWork unitOfWork = _bankAccountRepository.UnitOfWork; 16 }

 以下是国外开源项目Kigg的部分代码。

1、大致的思路与上面的代码类似,同样将DBContext的Commit方法暴露在Repository中,但是UnitOfWork通过IOC进行Resolve实例,从而形成较友好的Commit操作。

由于是通过IOC进行注入,为了保证Repository与UnitOfWork的DBContext为同一个实例,UnitOfWork的生命周期被设定为单例,这种方式不会有性能问题么?

 1         public virtual void Delete(IStory theStory, IUser byUser)
 2         {
 3             Check.Argument.IsNotNull(theStory, "theStory");
 4             Check.Argument.IsNotNull(byUser, "byUser");
 5 
 6             using(IUnitOfWork unitOfWork = UnitOfWork.Begin())
 7             {
 8                 //Removing story is supposed to be before Publishing Delete Event. However, because that Entity Framework
 9                 //set all related objects to null, a NullReferenceException will occure within event subscribers, as they
10                 //might access related objects such as PostedBy, BelongsTo, Votes etc...
11                 _eventAggregator.GetEvent<StoryDeleteEvent>().Publish(new StoryDeleteEventArgs(theStory, byUser));
12 
13                 _storyRepository.Remove(theStory);
14 
15                 unitOfWork.Commit();
16             }
17         }

 

根据上面的几个代码片段,再次引申之前的几个问题,求深度解答,谢谢!!

莫香邪。的主页 莫香邪。 | 初学一级 | 园豆:139
提问于:2015-03-24 00:12
< >
分享
最佳答案
1

我也来说一些自己的理解,可能不是很准确,仅作参考:

1. UnitOfWork和Repository的生命周期应该如何管理?大部分依赖对象通过IoC容器进行管理,Repository本身没有什么生命周期,准确的来说,应该是UnitOfWork的生命周期,我们在构成函数注入的时候,注入的是Repository映射对象,UnitOfWork是Using使用,把UnitOfWork对象通过构造函数传递给Repository,也就是说Repository依赖于UnitOfWork,当某个Repository操作完成的时候,比如GetUserById,会在Using块结束的时候释放掉UnitOfWork对象。
2. 跨Repository提交时应该如何工作?UnitOfWork是可以跨Repository调用的,也就是说,在Using块中,对多个Repository可以进行同时的操作,他们共享一个UnitOfWork,只需要在Repository完成操作的时候,调用UnitOfWork.SaveChanges UnitOfWork.Commit即可。
3. 有没有什么方式避免UnitOfWork中的SaveChanges暴露在Repository内部?UnitOfWork的SaveChanges不会在Repository出现,如果出现了,就说明你的Repository设计有问题。

收获园豆:50
田园里的蟋蟀 | 菜鸟二级 |园豆:423 | 2015-03-24 10:28
其他回答(4)
0

最近在学习DDD,但还没有真正实践过,以下是我的理解

1. UnitOfWork和Repository 的生命周期一般设置为线程生命周期,大多数做法是设置http请求的生命周期,即同一个请求,实例都一样;
不同的 Repository的 UnitOfWork应该是同一个;
其实Repository和UnitOfWork的生命周期都不重要,重要的是 它们两关联的 Db上下文的生命周期,它们应该关联同一个DB实例。

2.Repository主要是查询,可以把UnitOfWork独立出来,保存都用UnitOfWork来保存,Repository不直接关联UnitOfWork,上面说了,它们其实要关联的是DB实例。

3. 同上面点....

 

Qlin | 园豆:2403 (老鸟四级) | 2015-03-24 10:14

已对问题进行补充,希望能够得到更深度的解答,谢谢!

支持(0) 反对(0) 莫香邪。 | 园豆:139 (初学一级) | 2015-03-24 23:57

@莫香邪。:
1.个人比较倾向kigg,unitOfWork功能有一个 commit就行,不应该通过UnitOfWork暴露DbContext的其它操作。

2. Repository有DbContext,其内部能够Save 也正常,怎么控制 看约定要不要用,一般不推荐内部save,但是这个Save不能暴露到其它层里,其它层统一用UnitOfWork进行提交。

3. kigg的UnitOfWork 生命周期 如果单例 那他关联的DbContext也单例了? 感觉不行吧,应该还有其它生命周期的解析方式。

4. 看了 蟋蟀大神的不少文章,可以请教他

支持(0) 反对(0) Qlin | 园豆:2403 (老鸟四级) | 2015-03-25 10:42
0

Repository不能负责提交,也就是说不能有SaveChanges方法或者有IUnitOfWork的public引用

如果一个Service或者Handler需要提交的话,那么是把IUnitOfWork注入到具体的Service或者Handler,至于怎么保证多个Repository公用一个IUnitOfWork的问题,这个就是IUnitOfWork周期的问题了,一般最好是一次操作(调用一个service的方法或者执行一个handler)实例化一个uow,然后在service或者uow内部做提交,当然也可以用http那种方式,不过http弊端很多,比如你的系统如果有其他系统使用服务或其他非http方式调用的话就很麻烦,uow周期就不一样了,用线程共享也会存在问题,有的时候ms会缓存线程,所以共享线程那种方式每次用完就要清理uow

lawbc | 园豆:63 (初学一级) | 2015-03-24 10:29

已对问题进行补充,希望能够得到更深度的解答,谢谢!

支持(0) 反对(0) 莫香邪。 | 园豆:139 (初学一级) | 2015-03-24 23:57
0

趁早放弃Eric Evans 所提出的DDD,

那本书出版于差不多10年前,而思想差不多是20年前,

张口闭口领域模型,满脑子还是数据驱动(E-R)的思维模式

面向对象分析设计(OOAD),面向对象软件工程(OOSE)才是真正领域驱动的,和Eric.Evans完全是两种世界观

"领域""仓储"本质上都是领域概念,和具体的项目,应用无关,

也就是说"领域""仓储"的生产完全是独立的生产线,它们来源于具体项目,但高于具体项目

这两个词背后意味着巨大的重用价值,

那些整天研究DDD的大神们扪心自问,你的领域模型和仓储能在多大程度上重复使用?

缪军 | 园豆:202 (菜鸟二级) | 2015-03-27 10:24
0

工作单元的目的就是为了多个改变能一起提交到数据库. 你的程序需要哪些东西一起提交, 工作单元就和哪些绑定在一起. 如果你不在意每个改变都提交一遍数据库, 那不要也无所谓. 重点不在于你的工作单元是单例还是独立的, 而在于需要哪些东西一起提交. 实际上如果你的仓储背后对应的只有一个ORM的Session, 个人觉得仓储上就算直接带Commit方法也没啥问题.

另外Kigg的, 我没有看源代码, 但我觉得它应该是一个在IOC容器生命周期内的单例, 不太可能是真正的单例吧.

新一年 | 园豆:202 (菜鸟二级) | 2015-03-29 08:00
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册