领域驱动设计系列(二):领域Model?

前言

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

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

模型

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

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

领域模型

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

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

示例

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

1
2
3
4
5
6
7
public class Employee : Entity
{
public Name Name { get; set; }

public EmployeeStatus EmployeeStatus { get; set; }

}c

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class EmployeeService : IEmployeeService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly IEmployeeRepository _employeeRepository;

public EmployeeService(IUnitOfWorkFactory unitOfWorkFactory, IEmployeeRepository employeeRepository)
{
_unitOfWorkFactory = unitOfWorkFactory;
_employeeRepository = employeeRepository;
}

public void ChangeStatus(EmployeeStatus status, Guid employeeId)
{
using (var unitOfWork = _unitOfWorkFactory.GetCurrentUnitOfWork())
{
var employee = _employeeRepository.GetById(employeeId);
employee.EmployeeStatus = status;

unitOfWork.Commit();
}
}
}

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

1
2
3
4
5
6

public class Employee : Entity
{

public UserId UserId { get; private set; }
public EmployeeStatus EmployeeStatus { get; private set; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    public void ChangeStatus(EmployeeStatus status)
{
if (this.EmployeeStatus == EmployeeStatus.Pending && status != EmployeeStatus.Active)
{
throw new Exception("Only can Active when status is pending");
}

this.EmployeeStatus = status;
}

}

public class EmployeeService : IEmployeeService
{
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
private readonly IEmployeeRepository _employeeRepository;

public EmployeeService(IUnitOfWorkFactory unitOfWorkFactory, IEmployeeRepository employeeRepository)
{
_unitOfWorkFactory = unitOfWorkFactory;
_employeeRepository = employeeRepository;
}

public void ChangeStatus(EmployeeStatus status, Guid employeeId)
{
using (var unitOfWork = _unitOfWorkFactory.GetCurrentUnitOfWork())
{
var employee = _employeeRepository.GetById(employeeId);
employee.ChangeStatus(status);

unitOfWork.Commit();
}
}
}

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

后记

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

  
 DDD

领域驱动设计系列(一):为何要领域驱动设计?

前言

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

为何要领域驱动设计?

简化数据存储

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

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

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

更多了解上下文

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

统一语言(Ubiquitous Language)

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

1
UserService.Love(Jack, Rose)

但是我们业务人员很奇怪谁Love谁? 为什么要UserService?, 如果我们写成下面这样

1
Jack.Love(Rose)

还有如果我们用

1
Company.hire(employee)

来代替

1
companyservice.hire(company,employee)

这样我们就更容易让业务人员参与进来,而且代码可以更易于表示真实的业务场景。

  
 DDD

梦想的实现需要时间

梦想

从我记事后,在郝海东那段时间,中国足球在亚洲也算是风生水起,但是从那以后再也没有对中国足球有任何的梦想。教练换了又换,从外国教练换到国内,从国内换到国际,足球每况愈下。

这次亚洲杯,又一次燃起了中国球迷对中国足球的希望和梦想。

梦想是需要时间的

这次国足亚洲杯小组赛赢了沙特,赢了乌兹别克斯坦,然后中国球迷和媒体说,既然赢了两场,那么就全胜出线,打朝鲜,上半场踢的不错,主持人就说中国队是顶级时候的巴萨。可是大家难道没看到朝鲜下半场变成了顶级时候的皇马?

然后侥幸三场全胜出线了,球迷和媒体有开始说:“既然出线,那就夺冠”, 这让我想起02年世界杯的时候 “赢哥斯达黎加,平土耳其,小输巴西”,最后结果是三场比赛全输,丢了9个球,让我们认识到自己还是“三教九流”

从这件事情上, 我们可以看到大部分的球迷呀,媒体呀都想一口吃个胖子,我猜中国足球的现状和中国球迷以及媒体也有很大的关系,教练想干点实事,但是大家都要赢,有进步,大家其实很难看到,所以我猜每一个教练都只能是赌博式的,不然输了就得下课。如此下去,就像我们把程序功能做的看起来不错,但是代码里面千疮百孔,经不起时间的考验。

我觉得,我们都要有梦想,但是梦想都需要一个因素,那就是时间,那就是脚踏实地。那就是科学,每次国足输了,大家都说没拼,拼了就能理解,但是我想说,现代体育光靠拼是不行的,越是体育类比赛越是要靠脑子才能上一个台阶。

给国足时间,明天的比赛输赢有何重要?五星巴西没输过吗?

有梦想,有信心,但是务实更重要,持续进步才是最重要的。

明天彩票买澳大利亚赢吧,看球的时候就不那么紧张了,如果中国赢了,权当买酒庆祝了,如果中国输了,至少彩票还能赚点,你呢?

  

国足亚洲杯小组赛

好久没看国家队比赛,上大学时,那几年球市比较火,当时陕西队还在甲B, 那个时候陕西国力踢的很不错,我也几次买了最便宜的票去现场加油,陕西球迷的热情几次搞得比赛不得不移到宝鸡。

但是中国进入2002年世界杯后,那些熟悉的老将郝海东,范志毅,李明,李铁,李金羽等等都相继老去,而且国家队每每关键时刻给你顶不住最后3分钟,慢慢的我就不怎么看中国队了。

虽然不看中国比赛,但是新闻还是看的,之前知道中国队预选赛输了尽然都能进亚洲杯。

这次亚洲杯我正好休假,看了一场中国对沙特的比赛,感觉中国队和之前不太一样了,感觉有拼劲。确实看到了一些亮点,至于是不是佩兰执教带来的一些变化,这个还有待检验。第一场整体感觉上还是运气,因为沙特点球没进,而且我们进的球也很诡异,所以第一场比赛下来,感觉踢的还可以,但是还是觉得中国队有些侥幸。

今天打乌兹别克斯坦,这个球队实力确实不错,从历史交锋上,中国队就处于下风,观看了前30分钟,感觉中国队踢的不错,从传球到控球都有章法,但是上半场最后15分钟,感觉中国队的球员明显想法不一致,有些想进攻,有些不敢进攻,控球明显不如乌兹别克斯坦,而且失误增多,最后别对手攻入一球。

但是,我也并没失去希望,因为中国队前半个小时都占优势,回到休息室,我相信教练一定会做出调整,只要球队思路一致,找回之前的感觉,就一定有机会,果然下半场一开始,中国队就打的不错,后来佩兰果断的换人,起了非常重要的作用,大家可能觉得换人是神来之笔,但是我觉得换人后明显中场的球员敢于控球,而且前腰的队员明显敢于突破,这样就让球大部分时间都在对方战场,最终果然2比1逆转。

这次中国队赢球,着实也让我激动一把,因为看到中国队逆转的足球比赛太少了,听到主持人说:“留给乌兹别克斯坦斯坦的时间不多了”,我相信大部分中国球迷都是无比的激动,感觉压抑多年的一口气终于可以释放一次了。

中国足球要想提高,除了提升球员个人能力之外,球队的团结,技战术的提高,以及球队的信息和意志是更重要的。

期待中国足球越来越好。

写下此片日记的目的,就是中国足球的胜利了,都逆袭了,我还有什么不努力呢? 加油!

更令人高兴的时,今天心血来潮,堵了一把求,买中国赢,小赚了一把。

  

斯德哥尔摩30天-参加客户的年会

很有幸参加了客户的年会,听说大家都带家属,帅哥美女云集,所以我们都迫不及待。

年会的逼格还是非常高,先是大Boss讲话,然后请了个长的像瑞典王子的人讲话,一开始大家都被骗了,后来才知道是演员。

每一个人都端着红酒杯,风度翩翩的和别人聊天,我们当然作为嘉宾也受到了分外的照顾。 我们也和很多人聊了会儿天,对大家有更多的了解。

随后就是表演,晚餐。

晚餐过后,就是跳舞,虽然有位美女极力邀请我跳舞,可惜我不会,只好找个借口溜之大吉了。

还是上图片吧。

晚会的地方

正装

我们的帅哥,也是我们的朋友

三个屌丝,中间是我

高朋满座

假瑞典王子,骗过不少人呀

他们最佳员工,和我们密切合作的,主要是与外包团队协作,我们是不是也骄傲一下?

美女们,其中一个来机场接我们,谢谢了。

真心不错的合唱,天籁之音的感觉

姑娘,你的衣服真的穿好了吗?

和我一起做项目的帅哥,看看人家,以后你们再叫我帅哥,我就知道你们是违心的。

  

斯德哥尔摩30天-一日游

昨天周六赶工了一天,今天周日决定好好休息一下,把瑞典的几个主要的旅游经典参观一下。

市政厅

建于1911年,历时12年才完成,是瑞典建筑中最重要的作品。建筑两边临水,一座巍然矗立着的塔楼,与沿水面展开的裙房形成强烈的对比,加之装饰性很强的纵向长条窗,整个建筑犹如一艘航行中的大船,宏伟壮丽。
斯德哥尔摩市政厅位于瑞典首都市中心的梅拉伦湖畔,是斯德哥尔摩的形象和代表,也是该市市政委员会的办公场所。
斯德哥尔摩市政厅是一座宏伟壮观,设计新颖的红砖砌筑的建筑物,800万块红砖砌成的外墙,在高低错落、虚实相谐中保持着北欧传统古典建筑的诗情画意。市政厅的右侧是一座高106米,带有3个镀金皇冠的尖塔,代表瑞典、丹麦、挪威三国人民的合作无间。据说登上塔顶部,可一览整个城市的风貌。
市政厅内有巨大的宴会厅。宴会厅也有“蓝厅”的誉称。每年的12月10日是诺贝尔逝世日。这一天,诺贝尔奖金颁发后,瑞典国王和王后都要在宴会厅,为诺贝尔奖金获得者举行隆重盛大的宴会,表示热烈的祝贺。

瑞典皇宫

瑞典皇宫是国王办公和举行庆典的地方,斯德哥尔摩主要旅游景点。坐落在斯德哥尔摩市中心。建于17世纪,是瑞典著名建巩学家特里亚尔的作品。皇宫方正宽敞,中间有一个很大的场院,楼房围着场院而建,共有608个房间,比英国的白金汉宫还多4间。如今瑞典王室并不住在这里,而是住在在郊区的皇后岛,国王每天开车到皇宫来上班。

诺贝尔博物馆

这里能看到莫言的图像和介绍,还是很骄傲的!

斯堪森

斯堪森的介绍很好,但是去了确实没意思,比起我大秦岭野生动物园差太多了。

下面是修女吗?

一首 《we wish you a merry christmas》甚是好听。

  

斯德哥尔摩30天-船岛

今天是周日,由于星期中间都是相当的忙,今天决定自己转转,准备出发去船岛。

天气非常晴朗。

走了一段,就被一个户外的溜冰场给吸引了,那是真的冰,太酷了。

然后竟然走到了那天晚上喝酒的酒店

很快就到了船岛,美景无法言表,天很清,水很清,空气很fresh.

最后,偶遇瑞典皇家歌剧院

  

斯德哥尔摩30天-瓦萨博物馆

今天是周六。

朋友带我们一起去VASA博物馆,由于朋友的老婆一个人带俩小孩不方便,所以我们就一起带了一个出来。

瓦萨博物馆

VASA乘船的故事早就知道了,这个对一个管理者或者一个产品经理来说都很有教育意义

瓦萨王朝统治时期,瑞典是欧洲的强国之一。为了与劲敌丹麦、波兰对抗,称霸波罗的海,瑞典国王古斯塔夫·阿道夫斯二世要求建造一批新的战舰,并要求战舰航速要快、火力要强、装饰要华丽,因为这样才足以显示瓦萨王朝的权力、财富和战斗力。1626年初,作为其中最大的战舰“瓦萨”号在国王的亲自监督下正式开始建造。

瓦萨号内部
国王总是有太多要求。在“瓦萨”号建造期间,他不断下令依照他的旨意改变设计和建造要求。在“瓦萨”号的骨架已经安装好的时候,他下令增加战舰的长度。面对以性情暴躁闻名的国王,经验丰富的主造船师亨里克·哈伯特备感无奈,只得奉命行事。1627年,亨里克·哈伯特病逝。他的助手、没有太多经验的海因·雅各布森接替他主持建造。而此时,国王得知了丹麦建成双层炮舰的消息,于是他又决定,为原计划修建单层炮舰的“瓦萨”号增加一个枪械甲板,把它改建成“双层”炮舰。这样一来,“瓦萨”号便拥有了双排共64门舰炮,全长达到了69米,成了当时装备最齐全、武装程度最高的战船。但对于横梁和压舱物来说,在一个并不稳固的平台上安装的这些装备显得过高过长了,而且与平台的功能极不相称。但国王的命令没人敢违抗。
对“瓦萨”号进行的稳定性测试试验更像一场闹剧:让30个船员从船一端跑到另一端,以此检测船的摇动情况。试验中“瓦萨”号发生了危险的摇动,但对这个预警信号,海因·雅各布森却视而不见,还是决定取消试验,准备航行,结果—首航沉成绝唱。

瓦萨沉船

不亲身见vasa号是绝对感受不到这个船的霸气,高大,金色的船体,木雕堪称一绝。

圣诞来临了

回来时已经晚上,整个街道已经该是感觉到圣诞的气氛了。

  

斯德哥尔摩30天-客户现场

Stockholm的楼层都相对较低,客户所在的大楼稍微高一些,所以能看到城市的楼顶。楼等果然和国内的不同呀,典型的欧式建筑。

清一色的苹果电脑,可升降的桌椅站立和坐着办公自由切换,这个很不错。

工作了一天,讨论需求和设计,面对面的沟通,辅助白板效率是相当的高。

周五的下午可以喝着啤酒办公,这个不错,感觉我们是否也可以引进?(我的Surface其实也蛮霸气!)

  

斯德哥尔摩30天-吃饭喝酒

今天客户的大Boss请我们去吃饭。

喝酒

首先去了一个酒店,说是麦当娜当年来得时候住的,喝了一些香槟和酒,客户说很好喝,我时唱不出来啥区别,感觉没我大中国的太白和茅台好喝。原以为要吃饭,结果这只是喝酒的。

吃饭 (更多的酒)

喝完酒来到著名的老城区Gamla Stan, 原来吃饭就是点一个菜,剩下全是各种各样的酒呀,在国内是没有混着喝的,一下子喝了太多种类的酒了,感觉有点晕呀,看下酒瓶子都是一个人的呀!