DDD与Repository

Repository已经不是什么新鲜概念了。DDD模型自2004年提出,发展至今已经16年了。可是很多企业却没法实施,其缘由也很简单:DDD是基于需求的,而不少并不理解需求;DDD是容易实现的,而不少设计者并不会编程。这种状况就有一些两头不讨好,而若是有办法结合统一的话,则会很是好用。前端

 

学习Repository的过程当中,最早要进行的是思想的转变。在过往的编程过程当中,你们每每将目光聚焦在CRUD中,致使每一个程序员首先想的是我如何使用SQL实现我所要的目标。而在DDD过程当中,实践者应当将目光聚焦中功能上,首先将需求分解为若干个功能,而后再将功能进行组合。程序员

 

举例而言,某系统拥有组织机构和用户功能。组织机构树的每一个部门下属若干个职位,每一个职位都有用户担任,每一个用户能够出任多个部门的多个职位。这时,系统设计师告诉你这里要有如下功能:编程

部门管理(部门的CRUD,部门下职位的CRUD,职位与部门的CRUD)设计模式

用户管理(用户的CRUD)api

讲到这里,很容易看出,这里其实有四张表,也就是部门,职位,用户和用户职位关联表。表关系也容易理出,部门与职位1对多,职位与用户多对多。函数

 

若是你使用的是DAO思想(或者说分层思想),那么需求分析作到这一步也就结束了。你能够直接经过上述内容整理出你须要实现的接口,即每张表的CRUD。而后在前端实现一个界面,每一个界面调用相关的接口程序也就写完了。好比,其中一个接口多是这样的:学习

[Route(“~/api/SetPosition”)]ui

public void SetPosition(Guid userId, Guid positionId);设计

 

那么,如今问题来了。需求发生了一个变动,来了一个全新的需求,客户说我如今需求每一个部门的更改必须经过流程进行。即当部门信息发生变动时,必须层层审核,最后才经过后,才能在更新数据。这个审核过程甚至包含了一部分关键职位的人员变化。接口

这时,那个坑人的系统设计师又站出来了,给了你一系列功能变化表:

 

部门修改申请(部门修改申请CRUD,部门申请审核,部门申请同步到部门表,部门申请同步到职位表)

 

看上去,这个设计很美好“自顶向下逐步细化”分解的也很是舒服。可是,你仔细研究一下就发现这里有两个巨大的坑:

一、新建部门修改申请。在部门修改申请时,试问是否要将之前的部门数据复制到这张申请表中?若是你不复制,那了不起了,所有门全部手动数据所有要用户自行输入此表,那恐怕最终用户会和你闹的不可开交——这什么垃圾软件?!而若是你打算实现他,那我告诉你,这张可怕的部门表里,字段很少,100个(呵呵)。

二、若是你说功能1实际上是必须实现的新功能,和设计关系不大。那么你再观察,将部门申请同步到部门这个功能。他绝对能够细分为“修改部门”和“修改职位”两个子功能,而这两个子功能实际上是以前的接口就实现的。那么,你是否为以前的接口留下了复用性?仔细看看以前接口的实现代码,你就会悲剧的发现,70%的可能性那个接口是没法复用的,由于查询代码其实不太同样。

 

那这只是我随手说的一个需求变动,若是有更多的需求变化呢?那么虽然代码仍是可以复用一部分,设计空间释放也不会太麻烦。可是,仔细评判你的代码和设计,就会发现原来优雅而简洁的可复用设计的复用性愈来愈低,原来整齐而易读的代码的可读性愈来愈差。这就是人间悲剧。

 

而这时,Repository的思想从天而降,他也许可以为你可怜的代码带来一些让你惊喜的变动。若是使用DDD的思想设计上述内容,首先你须要肯定领域。显而易见的,这里的领域能够这样划分:

 

用户领域:添加用户,删除用户,修改用户,修改用户的职位,移除用户的职位

部门领域:添加部门,删除部门,修改部门,查询部门下的职位,查询部门下的用户

职位领域:添加职位,修改职位,删除职位,查询职位下的用户,将用户添加到职位中,将用户从职位中移除

注:这里,若是是我写代码,我极可能会把“部门领域”和“职位领域”合并。这个并没有不可,由于二者其实没有那么明显的边界。

 

在这个设计中,能够看到其实有些功能是重复的,好比说“修改用户的职位”和“将用户添加到职位中”。可是,在领域设计中,我却将其认为是两个不一样的功能,由于他们的主体不同。对前者而言,我先查出用户,函数的参数是“用户ID”和“职位名称”,这里使用出字符串的职位名称,即意味着对于用户领域来讲,他不须要认识“职位”这个类。对于后者而言,我先查出职位,函数参数是“职位”和“一个或者多个用户ID”。这意味着,对于职位领域来讲,他也不须要认识用户这个类。

这里能够看到,领域之间,耦合度很低。其实达到了最小知识原则所要求的内容。可是,实现过程当中,可能会有这样的疑问,将职位添加到用户过程当中,难道你不须要判断用户是否存在吗?固然,判断仍是要判断的,可是我彻底能够不认识用户这个类。经过将“用户职位关系表”中的“用户ID”字段与用户表中的“ID”字段作出外键关系,彻底可让数据帮我保证数据有效性。我只须要作一个简单的异常处理便可。

另外,耦合度低不等于不能耦合,在这里查询一次用户表,我认为也没有突破什么界限,因此彻底没有问题。

 

在设计完领域后,须要再设计边界,也就是说由哪些类将这些功能所有暴露给外界。这时能够这么设计:

 

部门类:添加职位,修改职位,删除职位,查询职位下的用户,将用户添加到职位中,将用户从职位中移除

用户类:查询我所在的部门和职位

用户服务类:用户的CRUD

部门服务类:部门的CRUD

 

这里,实际是将部门看成了职位的聚合。这只是我随手写的设计,没有实践过也不知道有没有什么问题。但我想大体应当是正确的。这时,我就将全部功能都经过这几个类暴露在外界。在考虑这些内容的状况下,再来上述需求时,问题就明确了,他须要新建一个领域:部门修改申请。

部门修改申请:经过部门新建修改申请,经过旧的修改申请新建修改申请,审核修改申请,将修改申请同步到部门中,将修改申请同步到职位中。

 

如今再来看以前的两个大坑。问题1实际上是规避不了。由于这个就是新功能,规避的惟一办法就是加钱,钱到位了功能也就到位了。而问题2确实就简单了,由于你能够直接调用暴露在“修改职位功能”将申请表中的用户给到对应职位,也能够经过调用“修改部门功能”直接将部门信息反向同步,而不须要考虑代码是否优雅,由于这里就是调用一个函数,并不存在优雅与否的问题。

 

再到之后,若是再有新功能,哪怕你仍是须要释放设计空间。但你在重构的时候,已经整理过的功能就不须要整理第二遍。你只须要交被释放出的设计空间所有放回领域中,重构的工做量大大减小。而这,就是我所看重的DDD的核心优点。

针对到实现层面,以前那些乱七八糟的领域功能,其实就是Repository,他的出现天然而又简单。你所须要的只是简单的变化一下本身的思想,多写几十行代码,仅此而已。

 

最后,稍稍总结一下。完成以上内容的核心和关键其实并非你对DDD了解多少。而真正有效的是你对需求了解多少,你认为需求有多少内容可能发生变化。对需求把握才是软件设计的核心。任何设计思想,设计模式都基于对需求的理解。我我的对软件思想的重要理解:

不基于需求任何想法都空谈,不理解需求任何代码都是胡说,不把握变化任何设计都是假想。

与君共勉。

相关文章
相关标签/搜索