走近领域驱动设计(一)

从我做过的项目来看,似乎欧洲已经有很多的公司开始实施领域驱动设计了,而我本人了解领域驱动设计也有些时间了。但是网上不管是文章还是代码,都显得太过高大上,一谈领域驱动设计,一大堆的概念一股脑的给你上上来,搞的有点晕头转向,而我想在一些中小项目中实施领域驱动也遇到了不小的障碍,大家对很多新的东西总是处于一种恐惧的状态。而且在真正开始实施时,也遇到一些疑问,所以想和大家交流学习,因此开始在此写写对领域驱动设计的理解。后续面陆续会有一些轻量的演进代码。

为何要领域驱动设计?

1简化数据存储

领域驱动设计有很多原因。谈到我为啥要在公司推行领域驱动设计,说起来还是很好玩的。因为原来基于数据驱动的开发方式,也就是传统的多层开发架构,大家定义了一堆DAL来操作数据,在.Net大家一般有两种使用方式,一种是用ORM像Entity Framework, 另一种想使用Dapper这样轻量级的Mapping工具,这些都要把关系型数据转换为对象。结果导致以下几种结果:

  • 没有正确的使用ORM, 导致数据加载过多,从而导致系统性能很差。
  • 为了解决性能问题,就不加载一些导航属性,但是却把DBEntity返回上层,这样对象的一些属性为空,上层使用这个数据时根本不知道什么时间这个属性是有值的,这个是很丑陋的是不是?
  • 如是又开始使用一些轻量级的数据方法,比如使用Dapper,然后自己写SQL语句,这本来是很不错的方式,但是大部分人的SQL能力实在不敢恭维,大部分写出来的SQL语句,甚至比Entity Framework生成的语句还差。

所以,我就想我们做项目,大部分处理的应该是业务,如何让程序员从数据存储,模型转换的大泥潭里解放出来?领域驱动设计就进入了我的视线。当然光从数据这个角度还不足以选择领域驱动设计,用一个NoSQL数据库是不是就解决了?但是NoSQL也有一些问题,比如MongoDB如何更优雅的保证事务以及数据的一致性等。

2更多了解上下文

我们很多软件的问题,大家都知道是需求的问题,也就是客户的需求我们很难理解准确。程序员也更加关注“HOW”——如何实现需求,而忽略了“WHAT”——什么是需求。最终做了几个礼拜甚至更长时间,客户却说:“What?! I told you.”但是客户告诉我们的,跟我们理解的是不一样的。比如客户说:“Great job, I love you!”这个Love肯定不是男女之间的Love。我们拿到的是一个客户的需求,但他的上下文是什么?比如说:“这个球打的好”,如果是在打篮球,肯定说的是篮球;如果是在打乒乓球肯定说的是乒乓球。而领域驱动设计里我们可以让业务人员更多的参与系统,更早的参与系统。

3统一语言(Ubiquitous Language)

业务人员和我们使用一样的语言。我们的程序,比如让业务尽量集中在领域里,比如在传统的数据驱动里,如果说Jack爱Rose,我们一般会这么写: C# UserService.Love (Jack, Rose)

但是我们业务人员很奇怪谁Love谁?为什么要UserService?如果我们写成这样:Jack.Love (Rose);

还有如果我们用:Company.hire (employee) 来代替companyservice.hire (company, employee), 这样我们就更容易让业务人员参与进来,而且代码可以更易于表示真实的业务场景。

领域模型

领域驱动设计里有很多东西,我们可以应用在各种各样的开发模式里,所以接下来说的一些东西,我们可以部分使用。

说到领域驱动的领域,大家肯定就要开始说Bounded Context,聚合,聚合根,容易把大家搞糊涂。我觉得应该先抛开这些概念,后面再来说如何设计聚合。先从简单的开始。

模型

过去,我们在多层设计里定义了很多Model。比如数据库的Model (DB Entity),然后为了不依赖数据库,我们又设计了业务的Domain Model,同时我们又设计了View Model,这样一般也没什么问题,职责也很清晰。但是有几个问题:

  • 我们要做很多的模型转换,转入转出。当然我们可以用AutoMapper来处理,但是AutoMapper的性能实在难以恭维,大家可以在网上搜索AutoMapper performance看看。
  • 领域模型成了一个单纯的DTO了。

领域模型

首先我们要看领域,就是我们尽量把业务聚合到一个领域里。比如我们要做一个功能,可以看到用户每一次的登录日志,那么这个登录日志其实就是属于用户这个领域里。

其次我们看模型,原来我们的模型都是只有属性,也就是贫血模型,贫血的意思就是没有行为,像木乃伊一样。但是实际上领域是我们要完成业务的最主要的地方,我们希望领域能够自治,也就是领域自己管理自己。

示例

比如有一个Employee,他的状态有Active,Pending,DeActive,业务上是Pending只能改为Active。

如果是贫血的Employee模型,我们往往代码如下:

但是上面的代码的问题就是领域没有自治。本来修改我的状态是我的事,你不能修改,外面随意修改我的状态是很危险的,比如Pending状态只能改为Active状态。所以如果不是贫血的模型,我们代码就会这样,让领域自己来管理。

因此可以看出,我们把业务代码尽量写在领域里让领域自治。

后记

其实领域驱动设计最难的就是设计领域 (Domain),也就是后面会说到的AggregateRoot 聚合。但是我想我们先让领域不再贫血,这样在传统的多层设计,数据驱动等架构里都可以使用这种模式。

事件驱动

今天主要讲一下如何用事件来解耦,我们有个项目的一个功能我觉得用事件的方式比较好,正好写篇博客,就不用专门给他们讲了。

解耦

说到解耦,我们很熟悉分层设计,比如上层依赖于抽象,不依赖于具体的实现。比如一个类使用另一个类,我们使用接口而不直接使用实现类。

为何用事件

1.SRP (单一职责)

比如我们一个会议室预定系统,我们的一个设备坏了。我们需要通知预定这个会议室的所有人。于是我们需要发邮件。

伪代码如下:

但是,问题来了。如果后来我们要说,如果设备坏了,我们要更改可用库存的数量,这时候我们是不是要在这里修改代码而引入InventoryService? 后来如果经理说设备坏了你们竟然不告诉我,你们要闹哪样?这个时候我们是不是要修改代码引入ISMSService.Info(Manager)?即使我们不考虑OCP原则,不考虑单一职责,我们程序员也会哭,我就DeActive一个设备,你要我做这么多事,我哪里清楚所有的功能?我就骂过程序员,你做这个功能呢为什么没考虑全!!!漏掉了这么重要的功能。

而问题是,程序员从来没考虑全过,因此我就想办法如何解决程序员不仔细的这个问题。

2. 事件驱动

因为我熟悉iOS的开发,我就想到了iOS的Notification Center. 那我DeActive一个设备,我就只DeActive这个设备,很SRP是不是?但是别的地方如何拿到通知?于是事件就自然的浮出水面了。如果设备被DeActive了,程序就只需要喊一声,老子把设备DeActive了,你们要闹哪样你们自己看着办,代码如下:

这样,通知会议室预定者的模块去通知预定者,给老板发短信的模块就通知老板就OK了。

以上只说为什么要使用事件驱动。但是只有概念是不够的,我们要代码呀!记得脸书的老总说过: “Talk is cheap,Show me the code!”所以接下来就show一下code。

实现思路

1.发出事件

事件顾名思义就是一件事情发生了,比如我要上头条,这不是一个事件,这是一个Command, Head Command,而我上头条了这就是一个事件Headed Event,事件就是一件事情已经发生了。好,先来一个伪代码:

所以我们只需在代码里Raise Event就可以了。

2.如何订阅事件

其实很简单,因为我们要实现的是同步的事件,我们只需要找到所有处理这个事件的实现类,然后调用所有就可以了。

如果国际章的妈妈关注这个Event, 我们就实现一个GuoJiZhangMotherEventHandler

如果我等屁民也关心这个事件的话,我们只需要再实现一个 PiMingEventHandler

看,我们可以任意增加关注事件的代码,不用修改原来的代码吧,说好的OCP没骗你吧? 那么问题来了,发出事件的人和接受事件的人怎么联系上的?在现实世界中,我们都是订阅报纸来看头条知道的,但是代码里我们就需要一个协调者了。如是我们就需要一个EventBus, 直接上代码吧

EventBus找出所有Handle这个事件的实现类,调用对应的Handle方法,我们可以通过Castle或者任何注入框架轻易的实现。

好了,哥只负责帮汪老师上头条,上完我发出了事件通知,谁关注谁自己处理去,我的代码也不用改。

我代码实现完了,如果各位还不知道如何实现一个同步的事件驱动架构,那拜托你们找个漂亮的妹子来问我。事件驱动架构我就只能帮你到这里了。

相关内容:

我们如何从领域驱动开发当中获益–王德水

领域驱动设计,遇见你之前 我们公司推行和实践敏捷已经很多年了,SCRUM已经成功应用于大部分项目,得益与业界敏捷开发大师以及国内很多优秀工程师的分享和宣传,我们使用了很多优秀的软件开发实践,比如测试驱动开发(TDD),行为驱动开发(BDD), 持续集成(CI)等等为我们带来了很多收益。由于我们公司以……

IOT 研究 技术趋势 洞见与思考 观察与技术趋势 软件开发 94 阅读

如何选择靠谱的软件外包公司

在信息化建设中,随着IT与业务进一步融合, IT成为推动业务转型、管理变革的重要力量。很多企业在10几年前购买的软件产品,已经无法适应日益变化的业务需求,需要根据企业自身业务模式进行定制化开发,以助力企业发展及业务转型。 传统企业通常没有专业的软件开发团队,组建IT团队的成本比较高,后续IT人才维护……

观察与技术趋势 软件开发 83 阅读

Mind Matter项目分享——设计不仅仅是设计

Mind Matter软件旨在促进企业的战略发展,并帮助推动战略的实践。其核心业务是开发下一代战略软件和服务。与各种类型和规模的企业组织合作,共同定义、设计和执行战略。 目前开发的软件作为一款简单精巧的协助工具,帮助用户定义、设计、讨论、决策和交付发展战略。 Mind Matter项目自2017年9……

技术趋势 观察与技术趋势 78 阅读

远程办公:谈谈我遇到的挑战与机遇

每每与身边朋友说起我在家上班,他们都会投来羡慕的目光,外加两个字:“真爽”。而我,只能无奈地回应:“其实也就那样了,并没有多爽。”这是心里话,但是他们只会觉得我矫情,得了便宜还卖乖,我也只能呵呵苦笑了。
我承认他们部分正确,是有点身在福中不知福,这也是人的天性吧,永远不满足。但是,我之所以如此笃定地说,在家办公没有那么舒坦,是因为这两年的远程办公经验让我明白,这种看似“爽”的工作方式,其实暗含着许多挑战,对远程工作者也提出了更高的要求。

敏捷实践 洞见与思考 软件开发 远程办公 99 阅读

引导客户不是靠话术 而是全然的负责

近期我们接了一个在线教育的客户,他们业务发展很快,旧有的系统虽然比较稳定但已经不能适应业务发展的需求,因此找到我们。充分了解需求之后,我们判断客户提出的任务不现实,在规定时间内完不成,既定目标不可行。于是我们将需求拆分,将功能实现的顺序重新安排:哪些在3个月内可以完成,哪些不行,同时接手客户的运维。

敏捷实践 观察与技术趋势 软件开发 远程办公 81 阅读

跟客户面对面确认需求是一种什么样的体验?

Matthew是个澳洲客户,前期有过很长时间的沟通和推进,我们对业务和项目需求目标大概了解了。但是针对第一个要发布的版本,要做成具体什么样的产品还是两眼一抹黑。故此,客户来我们办公室两周,专门讨论具体细节。期望经过两周的密集讨论,我们能有若干产出: 想想都挺多事情的。当然,理想都是很丰满的……过程不……

观察与技术趋势 软件开发 75 阅读

善用工具——成就高效沟通协作的团队

《敏捷软件开发宣言》  我们一直在实践中探寻更好的软件开发方法,身体力行的同时也帮助他人。由此我们建立了如下价值观: 个体和互动 高于 流程和工具  工作的软件 高于 详尽的文档  客户合作 高于 合同谈判  响应变化 高于 遵循计划 也就是说,尽管右项有其价……

敏捷实践 观察与技术趋势 软件开发 83 阅读

我的ODC项目经验分享

项目介绍:客户公司旨在为病人提供更加优质低价的治疗方案。其主系统联合病人、医师和医保公司,根据病人的病症、体检数据、过敏情况、生活习惯和过往服药方案等信息,结合其内部一套引擎工具,检查用药过程中的问题(Drug Therapy Problem)并提出给药建议。 在三年的合作过程中,我们不断丰富其主系……

观察与技术趋势 软件开发 92 阅读

敏捷实践系列(三):代码管理流程

我们已经从SVN切换到Git很多年了,现在几乎所有的项目都在使用Github管理。对于那些还在坚持使用SVN的,我实在想不出原因,权且称作守旧派吧。 Git的优点 Git的优点很多,但是这里只列出我认为非常突出的几点。 感兴趣的,可以去看一下Git本身的设计,内在的架构体现了很多的优势,不愧是出资天……

敏捷实践 观察与技术趋势 软件开发 79 阅读

敏捷实践系列(二)

大话西游里有一段因为没有沟通的经典, 结局如何大家都知道。 唐僧:你想要啊?悟空,你要是想要的话你就说话嘛,你不说我怎么知道你想要呢,虽然你很有诚意地看着我,可是你还是要跟我说你想要的。你真的想要吗?那你就拿去吧!你不是真的想要吧?难道你真的想要吗?…… 悟空:… 一. 敏捷项目沟通尤其重要 敏捷开……

敏捷实践 观察与技术趋势 软件开发 65 阅读