您的位置:首页 > 大数据 > 人工智能

DDD 应对具体业务场景,Domain Model 重新设计

2014-07-08 12:54 597 查看

DDD 应对具体业务场景,Domain Model 重新设计

写在前面

上联:no zuo no die why you try 下联:no try no high give me five 横批: let it go
上联:no zuo no die why you cry 下联:you try you die don't ask why 横批: just do it

  阅读目录:

自作自受

迷雾中的探照灯

我的错,我承认

再次出发

开源地址

后记

  上面那幅对联前段时间在网上还蛮火的,意思大家意会就可以了,这边就不翻译了,我个人理解,所表达的个性就是:in my life,no zuo no die。

  为什么会引入这个流行语?因为在应对具体业务场景,进行领域驱动设计的时候,整个项目的设计实现过程,所表达的就是这个意思:不作不死。 

我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践:伪领域驱动设计,只是用 .NET 实现的一个“空壳”,仅此而已。

一缕阳光:DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?:只是聚焦领域模型(认清各个部分的职责,让设计的焦点集中在领域模型中),文中关于领域模型的实现就是一个“渣”,仅此而已。

死去活来,而不变质:Domain Model(领域模型) 和 EntityFramework 如何正确进行对象关系映射?:走了个弯路,ORM 的映射关系及仓储的实现,应该是在本篇内容之后探讨,原因都是脚本驱动模式惹的祸,如果说脚本驱动模式是恶魔(特定的环境,也有好处,不能一概而论,这边只是一个比喻),那领域驱动设计可以看作是天使,心里想的是天使,却听了恶魔的话,为什么?因为它在你心中已根深蒂固,仅此而已。

拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?:在迷雾森林迷失那么久,自以为走了出来,其实是又走进了另一个迷雾森林,评论中和 netfocus 兄的讨论就证实了这一点。

自作自受

  我曾在上一篇博文的最后这样写道:“可能几天或者几周后,看现在的这篇博文就像一坨屎一样”。这篇博文指的是《拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?》,现在看来,正被我说中了。

  先回顾一下,上一篇博文所探讨的内容:Domain Model 到底如何设计?毫无疑问,领域模型的设计是领域驱动设计最重要的部分,关于领域模型的设计,从一开始的不理解,把领域模型设计的很贫血,然后业务逻辑都实现在了应用层,后来经过反思,把造成这种设计误区的元凶,怀疑到了 Repository(仓储)身上(后来证实,人家是无辜的),然后针对仓储,引入了 Domain Service(领域服务),把业务逻辑转移到了领域服务中(后来证实,完全错误的引用),只是把 Application 单词变成了 Domain Service 这个单词,其他无任何变化,以至于工作流程逻辑和业务逻辑完全分不开。

  造成以上的主要原因都是为了领域而领域,并没有实实在在的去思考业务逻辑和领域模型,后来认识到这个根本问题后,就抛开一切外在因素,比如领域服务、仓储、应用层、表现层等等,这些统统不管,只做领域模型的设计,让真正的设计焦点集中在领域模型上,然后再针对领域模型做单元测试。

  上面的思路听起来是还蛮不错的,至少听起来是不错,在上一篇博文中,后来,我是这样“忽悠”大家的:

回到短消息系统-MessageManager,需要注意的是,我们做的是消息系统,一切的一切都应该围绕 Message 领域模型展开, 在这个系统中,最重要的就是发送消息这个业务逻辑,什么叫发消息?不要被上面的面向对象所迷惑,只考虑发消息这个具体的业务,我们来分析一下:比如在现实生活中,我们要给女朋友写信,首先我们要写信的内容,写完之后,要写一下女朋友的地址信息及名字,这个写信才算完成,邮递员邮递并不在这个业务逻辑之内了,因为这封信我写上收件人之后,这封信相对于我来说就已经发出了,后面只不过是收件人收不收得到的问题了(即使我写好,没有寄出去)。也就是说邮递员邮递这个工作过程相当于数据的持久化,写信的这个过程就是邮递(发消息的业务逻辑),just it。

  观点富有辩证性,会让你认为“的确是这样啊”,呵呵。其实有一点我是说的不错,就是我们做的是短消息系统,一切的一切都应该围绕 Message 领域模型展开,消息领域模型中最重要的一个业务逻辑就是发消息(其他业务规则暂不考虑),那发消息的业务逻辑是什么?仅仅是我所固执的认为,往这条消息贴上一个收件人?你能接受吗?至少 netfocus 兄就不接受(具体请看上一篇博文评论),按照这种设计思路,消息领域模型的设计代码如下:

View Code
  发送消息领域服务:

View Code
  消息应用服务:

View Code
  这边再简单描述下发送消息这个业务流程实现,其实看下应用层的代码就清楚了,首先,UI 发送一个发消息的请求给应用层(相当于系统),参数为:标题、内容、发送人登录名、收件人显示名,应用层服务接到请求之后,先根据发送人和收件人的名称去仓储中查找相对应的用户,如果用户不存在,直接越过下面的发送操作,如果用户存在,则创建一个消息对象,在消息实体的构造函数中去验证这些参数的规则(比如参数不为空、字符串长度限制等等),如果验证成功则创建消息对象成功,首先这这一方面的改进之处就是,把收件人的赋值操作放在这边了,发送消息这个业务逻辑的体现其实并不是简单的赋值操作,其实这种实现更符合实际生活,比如我写一封信给女朋友,写好标题、内容、收件人和发件人之后,我并没有寄出,但是这封信已经存在了(符合信存在的标准),但是没有寄出,也就是说这个消息对象已经存在,只是现在这个对象的状态是未寄出,关于这一点,其实是和之前的设计是完全不同的,具体不同我也就不说了。

  换个行,要不然看着太费劲。我们接着说,消息对象创建成功之后(状态是未发),调用发送消息领域服务,进行业务规则验证(比如发送人不能和收件人相同,发送人一天之内不能发送超过100个的短消息等等),其实这才是真正的发送消息业务逻辑,正如领域服务所定义的那样,参数和返回值都是领域对象,也就是消息实体,发送验证成功后进入持久化或者基础服务发送邮箱,整个发送消息的工作流程就是这样。

  在上述发送消息工作流程描述中,需要注意的最重要的一点,就是消息状态的体现,也就是消息对象的未发状态和已发状态,这两个状态确定的前提这个消息对象是存在的,也就是创建成功的。在以前的设计中,如何体现这个发送状态的确定?答案就是收件人的赋值,之前认为,只有填写了收件人,那这个消息的状态就是已发送,其实这种逻辑有点天马星空。我现在个人感觉,消息对象的发送状态不能由它自身确定,也就是说不能由它自己的某一个属性确定,它应该是一个动态的过程,也就是在验证发送业务规则成功后,retrun 之后的那个消息对象,表示这个消息对象的状态是已发送的,因为它是符合发送消息业务规则并验证通过,那它的状态就是已发送。

开源地址

GitHub 开源地址:https://github.com/yuezhongxin/MessageManager

ASP.NET MVC 发布地址:http://www.xishuaiblog.com:8081/

ASP.NET WebAPI 发布地址:http://www.xishuaiblog.com:8082/api/Message/GetMessagesBySendUser/小菜

后记



  关于领域驱动设计实践的博文,也写了几篇,但是说句实在话,是有点对不住大家,因为下一篇都在为上一篇做一些解释或更正,希望大家在看得过程中保留一下自己的想法,不要被我给忽悠了。关于这一篇的内容,其实我现在已经做好下一篇更正的准备了,呵呵。

  下一步的计划是我是这样想的:现在一个发送消息用例基本上差不多了(可能还存在其他问题),然后接下来按照这种模式把其他消息用例加进来(比如消息回复、查看等等),看看会发生什么情况,可能会出现一大堆问题,这也是我想要的,与其有针对性的解决问题,总比苦思冥想的思考要好很多。

  MessageManager 项目设计到现在是没有数据库的(no datebase),在下面的开发设计过程中也会坚持这一原则。以前开发模式都是先根据需求建立表结构,然后再围绕数据库用面向对象语言做 SQL 的搬运工。可以幻想下,如果开发一个项目,在开发设计的过程中,完全没有数据库的概念(数据库在开发完成之后生成,只是数据存储的一种方式),会是什么感觉呢?我想那应该很奇妙。

  如果你觉得本篇文章对你有所帮助,请点击右下部“推荐”,^_^

  参考资料:

http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design-ddd/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐