寻找答案之DDD

转一篇领域驱动设计的文章,在探讨下设计
领域驱动设计实践

我觉得在他的设计中有很多问题,请大家探讨
1.在这个图书管理系统中的图书卡才是重点而不是借书的人,所以借书,还书的职责应该都由图书卡来充当。人用什么图书卡这个工具来借书,借书是图书卡的职责。
2.借书历史本来就是应该聚合在图书卡上的,设计者说是单独的聚合,我觉得不对。
3.它这个设计缺少了一个关键的值对象,那就是借书卡的状态,借书卡的状态,包括所有这张卡当前的借书条目,借书条目包括借的什么书,借书的日期,书归还的日期。我们需要根据这个借书卡状态计算这个借书卡是否欠款。至于这个借书历史,我其实觉得直接通过仓库查就可以了不必要聚合在图书卡上,也不必要形成单独的对象。自然也不需要保持这个内存空间对象的一致性,如果让图书卡聚合借书历史的,每当我归还一本书时,不仅仅要改变当前卡的借书状态,还要改变历史状态。
4.我有时候在想为什么要有值对象这个东西,如果从技术上考虑来说他是不可变对象,可共享,并发安全。我觉得最关键的一点就是值对象不可变,不会产生副作用.当你的脑海中有一个完整的活动的对象图时,修改一个对象的状态不会影响到其他对象的状态,不至于牵一发而动全身。
5.关于还书的处理,当你还一本书时,图书卡借书状态的变化跟业务规则有关,如果你还的书,已经过期。那么我不应该删除这个借书条目,只是把这个借书条目设置一个归还日期,等你还清罚款的时候才把这个借书条目从图书卡所借图书中删除,这样我就可以根据当前卡的借书条目来计算这张卡的欠款。但是这会有一个问题。就这其中有一个业务规则判断书是否过期的业务规则,在规格模式中,是否可以让值对象引用这个规格呢。
类图如下:



我个人认为它是有点问题。

它以Reader为聚合体方向就错了,Reader是阅读者,是参与者角色,而领域模型是排除参与者之外的客观世界的模型。

这个模型中Book毫无疑问是聚合根,其实找聚合根很容易,根据系统的名称,比如图书系统,那么图书是主要领域模型;论坛系统那么论坛帖子是主要领域模型,也就是聚合根。

如果在DDD之间了解一下四色原型,可能对分析需求有更好的区分作用。

我认为:他们把Reader作为聚合根,很大原因还是源自数据表建表思维,实际DDD等对象建模不是静止数据表的照搬照抄,如果说数据表是大而全,那么领域模型是找出系统中本质的简练的那几个模型,通过角色和场景这样活动,实现用例功能。

banq的观点和我的观点很多地方都不谋而合,但还是有出入的地方。“领域模型是排除参与者之外的客观世界的模型”,我觉得,排除参与者有点不妥,领域是现实的复写,少了任何一个都已破环领域,领域模型若果少了参与者模型,那么与参与者交互就无从下手了。

我觉得这里应该是系统思考的出发点,是从书出发,卡只是Reader的镜像。试想卡若果是没有身份信息,那么它能借书,还书,不觉得很奇怪么?再试想,若果有那么一样设备,不需要卡,只需要人走进去,就能识别身份,那么卡还需存在么?真正主导借书,还书的Reader本身。其实这里的卡就是一个Passport+user(Reader)的设计而已。

这里关键的想法是,书被谁借过,或者谁借过什么书。很明显这系统是以书为中心的,所以以书作为出发点,应选用书被谁借过。试想某系统不但有借书,还有吃饭,冲凉,打机等,借书只是其中一种而已,那么出发点则变成人了。

2011年01月27日 10:19 "SpeedVan"的内容
“领域模型是排除参与者之外的客观世界的模型”,我觉得,排除参与者有点不妥,领域是现实的复写,少了任何一个都已破环领域,领域模型若果少了参与者模型,那么与参与者交互就无从下手了。 ...

领域模型是排除参与者之外的客观世界的模型,或者说是排除了人之外的客观世界模型,如果你的系统存在人与物,那么物就是领域模型,而人与物的互动就是场景,就是结果(数据库背景的人常把这个结果当原因),就是需求要的功能。

从另外一个方面说,领域是有边界的,因为这个边界是人进行人为划分的,所以,才给人一种错觉:领域有主观成分。其实面向对象就是人为主观的,你关注它它就是对象,但你必须清楚,你关注它时,必须剔除各种人为带给它的影响,当然包括关注本身,所以,我们又假定领域是客观的。

>领域模型若果少了参与者模型,那么与参与者交互就无从下手了
领域模型就应该少了参与者模型,才能真正和参与者发生交互,我只有和你分清楚彼此,才能和你有更好的关系,如果不明晰彼此定位,如何有平等清晰的交互呢?

以上个人意见,纯属参考。


[该贴被banq于2011-01-27 13:34修改过]

那么就有点不解了,jivejdon中account是怎样的一个模型呢?在我看来,人也是实体而已,只是物是被动的,人是主动的而已。虽然人作为驱动者,但同时也是参与者。单单作为驱动者可以不作为领域模型,但作为参与者时就不能舍弃了。若果内存是可以图形化,那么我认为,在可以在内存中看到“我(内存中的我)在借书”这个过程。

分析建模是一个先分后合的过程,设计过程是分,代码过程是合,代码是结果,就象我这顿吃蔬菜,晚上拉屎了,你指着我的屎说,有肉,这能否定了我某顿吃蔬菜吗?我可能其他顿吃肉了,最后结果是有荤有蔬。

设计过程就是一个切分过程,然后落实为代码组合运行,我想DCI架构已经完全昭示了这个设计原理。

如果不理解,可以借鉴看看DDD中的Cargo货物案例,它有没有把货运运输者或货运订户等等介入建模呢?

当我们谈领域时,已经默认把人的因素排除开了,因为领域只有对人来说才有意义,人是在领域范围之外的,如果人也划入领域,那就是测不准定理了,一团浆糊,所以,领域建模不是随便程序员都可以做的,否则就是一门纯技术活了,它和建模者的分析能力强弱有关。

DDD是一个面向对象思想延伸,如果没有足够的面向对象经验和基础支撑,就会出现象楼主提出那个案例情况,就象我们学习数学要大量学习加减乘除一样,不能以为面向对象就一句“大道自然”就能知道,它也是一门专业学科,国外发展了这么多年,国内大学期间都学数据结构算法等这些基础再基础的知识,就凭工作这几年能够一下悟出搞懂DDD,我觉得是不可能的。

以上只是我的个人想法,如果你还无法理解,有可能我的解释偏差了,也不用再讨论了,因为角度不同,纠结就没有意义了。


[该贴被banq于2011-01-28 09:59修改过]

2011年01月27日 13:34 "banq"的内容
领域有主观成分。其实面向对象就是人为主观的,你关注它它就是对象,但你必须清楚,你关注它时,必须剔除各种人为带给它的影响,当然包括关注本身,所以,我们又假定领域是客观的。 ...

原以为这个例子够简单,所以昨天晚上代码实现了图书馆借书领域的原型,结果由于1个微不足道的bug,凌晨5点多才睡。白天打开楼主提供的链接,查看写那篇文章的作者的模型设计,与我设计的原型,竟然相差极大。作者来自于www.agilejava.com,应该是位经验非常丰富的程序员,使用了DDD提供的术语进行辨别各种实体、值对象、服务。我是根据自己正在摸索的一套方法来建模,借鉴了四色原型与DCI的部分思想,但不采用任何晦涩的术语。

根据我多年借书的经验来看,作者建立的模型似乎并没有对我这个“用户心中的需求”进行简洁、准确的描述。该作者似乎将“以用户心中的需求为中心”等同于“以用户为中心”,这两个是不同的概念,不能忽略其差异。这里想与banq老师重新确定几个最核心的概念。"领域模型”、“领域”、“用户需求”。

之前我有个说法,“领域相对于用户需求是主观的”,banq老师当时是同意这个观点,甚至修改了“领域是客观的,业务是主观(用户需求)”观点。现在我感觉banq老师的观点更正确,不过缺乏了足够的铺垫或者条件说明。如今,对于领域、领域模型、用户需求,我有了一番新的思考,对原来的观点进行了部分的修正。

“领域相对于用户需求是主观的”,这句话隐含了一个假设或参照,我们对领域认识的来源于用户需求,可是用户需求来自哪里呢?用户需求同样来自于领域,来自于对领域的诉求!之前我的另一个观点,“领域是对用户需求的素描”,这句话同样隐含了一个前提,用户需求接近于领域的本质,我们对领域的认识可以通过对用户需求进行素描而实现。

这里我重新确定领域、领域建模、用户需求三者的关系。

1)领域是客观的。
2)领域模型是主观的,体现了程序员对领域的认识,是程序员心中对领域的素描。
3)用户需求是主观的,体现了用户对领域的认识,是用户心中对领域的素描。
4)这点非常重要,就是“领域模型”与“用户需求”的关系,要展开来讲。

“用户需求”是对领域的“素描”,用户的需求来自对领域的“诉求”,这些诉求往往是深刻的,因为其来源于用户对领域长期观察和使用的经验,比起我们程序员,一般更完整、更真实地接近领域的本质。我们对“用户需求进行素描”,就是“借鉴用户的宝贵经验”,可以更快、更好地素描客观领域,这可以说是一条认识未知领域的捷径。但是当用户需求不明朗或不清晰时,我们需要超越“用户需求”,对领域进行深入的摸索,去寻求更清晰的视角,对领域进行刻画。

最后,再谈点这个图书领域的建模,是否应该包含参与者。我在实现原型时认为借书卡Card也是一个模型(DCI的D, 四色原型的PPT),同时也是一个角色(DCI的I的参与者, 四色原型的Role)。因为要实现“无卡还书”的场景,必须保证借书卡的唯一性,我不得不用“单例工厂”来管理Card的创建。不过,想想这与现实也不谋而合,在学校中,我们学生只能使用借书卡,却不能创建和注销借书卡。

我理解banq老师所说的建模时要排除的参与者,在这个案例中是指Reader,而不是Card,如果我的理解没有错,那我是赞同这一点。这个观点对于没有类似思维体验的人,是有点晦涩的,但是非常有意义的,我愿意这里解释一下,banq老师是否也是从这种角度出发?

领域模型(程序员对领域的认识)要包容用户需求(用户对领域的认识),这就像一个杯子要装满一杯水,我们在制作杯子时,制作的是空杯子,即要把水倒出来,之后才能装下水;再比如,一座房子要住人,我们在建造房子时,建造的房子是空的,唯有空的才能容乃人的居住。从这个意义,领域模型要将用户(参与者)排除在外,这是《老子》的“无之以为用”观点的一种应用。

我想banq老师更熟悉的例子应该是山水画(我不熟悉)。山水画的留白,体现的意境,也是领域建模“似乎没有用户”要达到的意境,即“无墨之韵”、“此时无声胜有声”、“无为胜有为”。不过,要达到这个境界,确非易事,需要的也许是毕生的努力。

赶紧睡觉,不然明天又起不来了。呵呵。

谢谢Jdon007的解答,我提出这个例子就是因为我觉得这么有经验的作者对这个问题的建模却完全呈现一种静态的感觉。没有真正深入的理解这个需求的本质。
这参加工作几个月以来,我们的技术总监每每谈其业务,都能体现一种很强的面向对象的思维方式,我就觉得面向对象的学习重在面向对象思维方式的培养,所以我反复的发这种有需求的帖子,希望看看高手们对需求的理解角度和方向。正如Bang强调的,我在找那种适合自己的分析行思维,按照那种思维方式能直接不带任何别扭的直接映射到程序语言的表达上。
Jdon007能把你写的代码发给我嘛。谢谢。我的邮箱512668804@qq.com
高手们能介绍几本跟讲这种对象分心的书嘛,谢谢

嗯,经你们这么一说,也挺有道理,好吧,我继续洗脑。这里的领域模型是指客观的物(由人认识出来的物)。越接近客观物的领域模型,若果对其进行操作,则越得到接近现实的响应?这样说对否?也就是说领域模型并不是在内存的虚拟空间的一切(虚拟空间,我之前的理解,类似网游一样),而是这个虚拟空间里面,除了操作者外的所有响应实体?简单说就是,并不是领域的模型,而是领域内物的模型?还是说领域本来就是物的内聚空间,与事无关?

回到图书馆领域的例子中,若果没有了Reader,那么借书和还书职责是谁的职责呢?而我对卡的理解是:卡只是Reader的一个镜像而已。刷卡相当于登录,而卡并不能借书还书,登录成功后,相当于获得内存中的某Reader控制权。因为没有过多的行为,所以我们可以把登录和借书,在一次刷卡中完成。

发现自己对图书馆系统理解有误,改变一下,图书馆系统与Reader无关,而是与Manager(图书管理员)有关(是Manager使用系统吧,Manager把书借出还入,当然若果reader也可以使用系统的话,那么上面也可能存在的)。而刷卡只是把信息输入而已,并非登录。那么我改变一下说法,Manager(参与者)是否是领域模型的一部分呢?banq的回答是,不是。

我之前的想法:“Reader(在图书馆)借书”这整一件事都属于领域模型。Reader是角色模型,借是业务模型,书是物模型,而书和Reader都归为实体模型,于是我就认为了这些全部就组成领域模型了。而banq说,领域模型只包括物模型而已。

而在DDD一书中,中文版的326页也谈到去掉customer,但其考虑过程是“运输货物的交互过程中并没有经常涉及到customer类,而且现在可以直接使用customer agreement,所以customer的基本模型是相当通用的。”,最后也没有把customer去掉,而是把它放到通用子域中,而这个子域也是模型的一个模块。那么这个customer到底是怎样的一个模型呢?(这里的customer和reader很像,而cargo与book很像)在DDD一书也感觉不到只谈物,它也谈到很多业务模型。

DDD一书也有这样的一句话“如果我们把图的遍历机制和领域模型混在一起的话,那么会造成两个问题。首先,模型会与解决问题的特定方法绑定起来,从而限制了将来的选择。更重要的是,组织模型将会变得非常复杂和混乱。”这一句话,跟banq说的是一个意思了,这样看来,领域模型是与谁用和怎样用是无关的模型?

归纳起来说就是,领域建模是建立虚拟模型让我们现实的人使用,而不是建立虚拟空间,去模仿现实。这样说是否正确了呢?这是我根据你们的描述,作出的理解,同时也可能是我误解的原因。

[该贴被SpeedVan于2011-01-29 02:18修改过]
[该贴被SpeedVan于2011-01-29 02:42修改过]

各位辛苦,搞了通宵。

我应该再补充一下我前面一个观点:领域模型是排除参与者之外的客观世界的模型,或者说是排除了人之外的客观世界模型。

排除参与者,不是说在模型图中没有它,而是不要让参与者占据主要位置,特别是作为聚合根。

如果作为聚合根,那么就失去了系统领域之间的区别特征,因为软件系统就是一个人机交互系统,都是以人为主的活动记录或跟踪,就象JiveJdon中的Account,如果我把它作为论坛的聚合根,大家有什么想法?按照这个思路,Cargo把Customer作为聚合根,等等....,这些系统之间就没有区别了啊,都是人为聚合根为核心为焦点的系统了。

话不能多说,多说就有偏向,有些偏执,不知我是否表达清楚我的意思。


2011年01月29日 08:43 "banq"的内容
排除参与者,不是说在模型图中没有它 ...

这···那么是我误解了。

摘录
不要让参与者占据主要位置,特别是作为聚合根。

这句话我可以理解。而Account作为聚合根,我也是不认同的。在DDD中,有这么一个词“核心领域”,而这个词的意思,好像正好跟banq提到的“排除参与者之外”的思维是一样的。它就是无视一切通用子域,最为核心的一部分。像论坛的除人以外的所有。而聚合根就是这样的核心。

摘录
话不能多说,多说就有偏向,有些偏执,不知我是否表达清楚我的意思。

是否够清楚就不知道了,但每次总是可以理清一点头绪或者修正误解。指“道”者,带给我们的不单是知识,还有经验,而经验正是新来者所缺的。不知道banq你是不是怕了别人说你偏执,我倒是觉得别人对我多说一点,才能了解对方更多的想法。我欢迎所有人都来向我讲述新思想,这对我发散思维有很大好处。哪怕是把话说成一本书,我也照看无误,当然前提是有时间。DDD都有说了,思想的隔离才是可怕的。

[该贴被SpeedVan于2011-01-29 11:51修改过]
[该贴被SpeedVan于2011-01-29 12:06修改过]

不要急于判定一个事物的好坏,更不要有非此即彼的惯性思维。就拿明眼人咋一看Reader有借书()、还书()这两个职责很符合客观现实这一点就能体现将Reader这样划分在边界内的意义所在了。并且,本案例提供的需求来看,这样的设计并没有什么多大问题啊。
楼主第三点提出来的问题很好,但是,以目前所知需求并没有反映借书状态来看,提出的这个问题对于客户并没有实际意义。弄不好,反倒是画蛇添足。

大家有没有想过,为什我们一定要认为一个领域需要一个模型呢?我觉得这个思想是错误的。四色原型是一种分析需求的方法论。四色原型可以概括为:什么什么样的参与者(可以为人或组织,也可以为物体)以某个角色在某个时刻或某段时间内参与某个活动。我认同这种认知世界各种行为的方法论比较科学,至少我目前还没有找出什么破绽。而从四色原型可以看出,世界是动态的,这让我联想到,任何一个软件系统也应该是动态的。因为一个软件系统就是能够完成部分特定领域内的活动。那请问“活动”能用模型来表示吗?比如一个图书管理系统,请问现实世界有图书管理系统吗?没有!图书管理系统只是我们人类为了方便我们进行和图书相关的操作(活动)而设计出来的一个系统。我觉得按照四色原型的理论,参与者非常重要,如果没有了参与者,那活动也就不存在了,并且我觉得参与者没有主次之分,少了任何一个,活动就进行不起来。我看到大家在讨论参与者是否应该包含在领域模型内的讨论,我觉得很奇怪为什么会有这样的想法,因为这让我感觉到模型比活动重要。而实际上我觉得恰恰相反,模型是不存在的,唯一客观存在的仅仅是活动!所以我觉得,我们在分析需求是,不要用模型的思想,而应该总是以活动为出发点,然后分析有哪些参与者,完全用动态的思想去考虑问题。脑子里不要有模型的概念,从而就自然就没有参与者是否包含在模型中的问题了。其实世界上的任何活动都很简单,就是参与者相互协作参与某个活动,完全和模型无关。而这也是四色原型的表述。

大家有没有想过,为什我们一定要认为一个领域需要一个模型呢?我觉得这个思想是错误的。四色原型是一种分析需求的方法论。四色原型可以概括为:什么什么样的参与者(可以为人或组织,也可以为物体)以某个角色在某个时刻或某段时间内参与某个活动。我认同这种认知世界各种行为的方法论比较科学,至少我目前还没有找出什么破绽。而从四色原型可以看出,世界是动态的,这让我联想到,任何一个软件系统也应该是动态的。因为一个软件系统就是能够完成部分特定领域内的活动。那请问“活动”能用模型来表示吗?比如一个图书管理系统,请问现实世界有图书管理系统吗?没有!图书管理系统只是我们人类为了方便我们进行和图书相关的操作(活动)而设计出来的一个系统。我觉得按照四色原型的理论,参与者非常重要,如果没有了参与者,那活动也就不存在了,并且我觉得参与者没有主次之分,少了任何一个,活动就进行不起来。我看到大家在讨论参与者是否应该包含在领域模型内的讨论,我觉得很奇怪为什么会有这样的想法,因为这让我感觉到模型比活动重要。而实际上我觉得恰恰相反,模型是不存在的,唯一客观存在的仅仅是活动!所以我觉得,我们在分析需求是,不要用模型的思想,而应该总是以活动为出发点,然后分析有哪些参与者,完全用动态的思想去考虑问题。脑子里不要有模型的概念,从而就自然就没有参与者是否包含在模型中的问题了。其实世界上的任何活动都很简单,就是参与者相互协作参与某个活动,完全和模型无关。而这也是四色原型的表述。

2011年03月05日 13:08 "tangxuehua"的内容
我们在分析需求是,不要用模型的思想,而应该总是以活动为出发点,然后分析有哪些参与者,完全用动态的思想去考虑问题。 ...

分析时我大致也是这么分析的。只不过,对领域模型的定义彼此有所不同,我定义的领域模型不仅描述了领域中的各种实物(实体或模型),也描述了领域中的各种实景(服务或活动)。实景中包含了实物之间如何交互,交互的表达手段可以是赋予实物以角色,也可以让实物之间相互传递消息(比如事件)。

简而言之,分析时,先关注领域实景,从实景的交互中找出领域的实物。领域模型包含了领域实景与领域实物,是一个动态的系统的概念。这与我在未接触计算机编程之前对模型的认识基本是一致的,其实何止软件开发需要建模。

但设计领域模型时却与此刚好相反,从实物开始,赋予实物以交互能力,在此之上,描绘领域的实景。分析与设计的思路上呈现对称之美。分析以直接的方式,从动到静,从实景找实物;设计以间接的方式,从静到动,赋予实物产生实景的能力。

一个接近客观的领域模型应该相对独立于用户,也就是说让领域内在的规律来决定领域模型该如何变化如何发展,而非用户的意志,也唯有如此,领域模型才有可能包容用户参差多态与不断变化的需求。

领域模型应排除的不是参与者(角色),排除的是用户(使用者),但排除的目的不是排除,恰好相反,是为了尽可能地包容用户的需求。(用户的需求不等于用户。)

这有点悖论的感觉,经济活动、历史活动,明明是人参与的活动,其自身规律(领域模型要表达或体现的实质),却非为人所左右。在另一个帖子《领域驱动设计之我见》,有相似的讨论。

这个说法未必会获得大家的认同,在实践中也可能寸步难行,何必自寻烦恼?用户指东就往东,指西就往西,能拿到钱就行,这个重要得多。其他啥的,领域规律是啥,有必要去琢磨吗,有时间、有精力、有能力去探求吗,再说了这与我何干?