整理关于牛人们对图书管理系统领域建模的精彩讨论,以此希望大家学习下别人是如何思考的
Posted on 2011-06-21 23:20 netfocus 阅读(6772) 评论(16) 编辑 收藏 举报关于图书管理系统的业务大家都应该比较了解了,主要的核心业务是:用户持图书卡去图书馆借书或还书。下面是他们几个人讨论的最经典内容,我特地整理出来供大家可以集中的观看他们的讨论。其实在我看来更是一种世界观与世界观的碰撞,我想借此表达的思想是:代码不一定要写很多,但是思维方式或者说世界观一定要正确,否则方向错了,就什么都错了。
以下是讨论的详细内容:
Jdon007:
1、借书人(Reader)与借书卡(Card)不是镜像,借书人(Reader)是借书卡(Card)的使用者。
2、认证是认证,跟借书卡没有关系。在这里相当于借书人有没有资格拿到卡,也就是如果你不是这个学校的的师生,就拿不到这个学校图书馆的借书卡。
3、借书卡之于借书人,相当于信箱之于收信人,这是不同的概念。
4、可以考虑在应用层做一个镜像,也就是借书人本身的镜像,对其“借书资格”进行审查,此时可以建立一个Accout类作为其在系统中的镜像,至于你如何审查这个Accout, 如指纹识别,身份证识别,手机号码识别,学生证识别,DNA识别,这个都是对Accout(即Reader)借书资格的审查,是该领域核心之外的东西。
5、借书卡,是一个借书的工具,就像信箱,是一个收发邮件的工具。用户User在系统中的镜像Accout,与这并无直接的联系。对“用户进入领域的资格”与“领域本身”是相对独立的。路人甲的房子与房子的房产证(路人甲的镜像)是不同的,日后,房产证可能丢失或转移到路人乙手里,但房子依旧,领域依旧。
6、你一直强调用户在领域模型中存在镜像,但是“建立模型时把用户至于领域之中”与“用户使用领域模型时进入领域之中”是大有不同的,而不是形式上的差异(说法不同而已)。
7、如果领域模型将包含用户的镜像,以人以自我为中心的潜意识和习惯,几乎必然以Reader为核心模型,一切都围绕着Reader转,很难客观地描述领域,比如张逸先生将canBorrowBook的方法放在Reader之中,可是能不能借书不是Reader说了算,是BorrowingTerms规定的。
8、上面的这些解释“看得见”,只是一些具体的描述,“看不见”的“无之以为用”的思想更值得好好领会和运用。毕竟,“看得见”的东西是有形和有限的(直观和感性非常重要,但不能止步于此,虽然我们很多时候也只能止步于此,想再往前走几乎无路可走,因为超出经验的认知之路是极其艰难的),如果换了一种形式,可能就“看不见”了,只有“看见”“看不见”的东西,以后再遇到这种情景,不管其以任何表象出现在我们面前,我们都可能看到真相,不再拘泥于表象或受表象之惑。当然,这种悟性的成长要长期的坚持和努力。
9、这些只是我个人的想法,正确与否,还需要时间和实践的检验。大家如果有不同看法,可以畅所欲言。
SpeedVan:
1、你认为是使用者,而我认为这是登录方式,使用卡和使用DNA是同一样东西,方式不同而已。一个是卡码,一个是DNA密码,都是唯一的。“借书人(Reader)是借书卡(Card)的使用者”,这里的本质是,我们不是使用卡借书,而是使用卡登录,只是因为行为单一,所以可以把登录和借书合并而已。试想,难道罚款,越境登记,这些是一个系统的话,那么这些都不是人在做事,而是身份证?
2、借书卡是申请而来,不是借书时才确定的,而已一开始就已经在现实判断有没有拥有权——相关手续后得卡,这里相当于注册。如果不是这个学校的老师,那么是在申请时,不能得到卡,而不是借书时不能得到。
3、没有卡就不能用DNA来借书?没有信箱,不能用其他来收信?
4、若果把Reader放到应用层,这是绝对的错误,请注意,account其实是混合了passport和user的概念,所以会被迷惑,登录属应用层,这是对passport而言的,但在领域中是user,我之前就说过帐号和用户的区别。
5、我根本观点是,它本身并不是为借书而存在的,是登录,是因为行为只有借书,所以可以一并启动借书而已,但这当中是两步过程,不是一步。(也可以认为是三步,借书完成后,直接登出)。
6、请你看看DDD中Cargo对Customer的说法。注意:关键一词——实体。
7、注意,是不是你没有考虑过第一人称和第三人称的思维的区别。自我为中心是第一人称思维,那么也请你用一下第三人称思维来思考,把Reader当作实体来看待。“张逸先生将canBorrowBook的方法放在Reader之中”,这里为啥写成canBorrowBook,若果是borrowBook(借书)行为的话,这行为正是那个实体的行为,有何不妥?就如登录了的user能够发表帖子一样。
8、把刷卡只看作借书,这不表象?
若果排除人的话,Cargo中的customer早应去掉了,为啥还存在呢?customer也是聚合根,只是没有其他东西内聚进去而已。我感觉你把领域的核心模型——核心领域模型,理解为领域模型,这两者是不同的,DDD一书中“精炼”一章有说明。Customer最后被归为模型中的能力层里。
其实我所说的Reader和你们所说的Card和Account极为相似的,可以说地位很相似,迟点我也放上我根据我理解的代码。
Freeboy:
可以这样理解,借书者是客户,图书馆是资源,提供服务。而卡是一种对客户的借书的行为的控制。现实中应该是这样,一个人想去借书,必须先去图书管登记,而卡则是一种形式。有些人可以一个月可以借3本书,而有些人可以借6本书,有些人可以借英文书,而有些人则只能借中文书。所以卡有不同类型的卡,不同级别的卡,收费或者免费的卡。这时候的卡隐含着权限的意思。它就犹如角色,或者部门一样,你得在这个角色,或者部门才会有这种权限。卡只是人的一种映射,就如SpeedVan说的。其最终目的还是对客户的权限的审核。即可以借什么样的书,借多少书,借多少时间等等。
SpeedVan:
嗯,freeboy能了解我的理解,也是我为啥理解为登录(认证)。其实可以这样去解析,若果没有卡,直接人去借书不行吗?其实是可以的,但是必须每次都要审查一样确认身份。卡只是一种方便手段,就如DNA一样,回想一下没有卡的年代,呵呵。无论用DNA的图书馆,还是卡的图书馆,我的基本模型都不用改变。其实我为什么认为卡是人的一种映射,你把Card这个名字改为Borrower就能明白了,因为卡(你理解的卡)和人的共通点在“借方”这个角色。而你理解的卡像人一样,而我不这么认为,多人使用的系统,肯定会存在身份确认,到底在那一步呢?就在刷卡那一下,系统知道了谁在借书,或者书被谁借了。
再拿个例子,登录式的留言版(假设帐号和留言信息是一同输入的,留言后登出),留言版只有留言这一动作,跟图书馆只有借书这个动作一样,留言版核心是“言”,图书馆是“书”,那么登录就如刷卡了。
Jdon007:
1、首先我是根据我在学校多年的借书经验来思考,不是DDD的术语,我觉得这些术语在概念上一定程度是具有入侵性的,甚至妨碍我进行独立的思考。唯一限制我思考能力是我对自然和现实的认知程度。我学了四色原型,随后就开始淡忘了四色原型,学了DCI,随后也开始有点淡忘了DCI,取其意忘其形,似乎一直是处于“得意忘形”的状态,年纪大了,记忆力开始退化,开始有点健忘,很多时候能说出其背后的意义,却想不起来最初的名字。
2、书就是书,为何要加一个书是实体或书是聚合根这些多余的概念?如果这套概念有利于统一管理领域模型,并驱动技术实现,那么这也是之后的该做事情,不要用这些概念来思考,领域该是怎样的,就是怎样的,领域之多之丰富,任何一套理论意图驾驭在现实之上或自然之上,都会捉襟见肘,顾此失彼。“理论是灰色的,而生活之树常青”。
3、卡的权限是借阅规章制度规定的,持卡资格与卡本身的权限或类型,是相对独立的。别的同学可以使用我的卡去借书,卡是借书工具,尽管此卡已经注册了我的名字,但是可以更改卡的拥有人,卡的借书功能依旧。
4、信箱是信箱,收信人是收人,即便收信人已经搬家或者去世,信箱依旧存在;即便学生已经毕业,不再持有该学校借书卡的资格,借书卡的权限、借书卡的借书功能,依旧存在;即便员工已经辞职,这个公司规定的各个部门的不同级别的职位访问各种资源的权限依旧存在。即便你做了皇帝,不要以为你不在了,皇帝这一职位或角色就不在了,青山不改,绿水长流,江山代有新人出,一代新人换旧人而已。
5、也许这座图书馆从未有人来借过书,也就是不管用户是否存在,但是“借阅规则”依然存在,其中规定包含了对各种类型卡的权限的的规定。用户持卡,只是“拥有”并可以“使用”这张卡。对用户是否有资格拥有某类型的卡,则是对用户进入领域资格的审查。
家里有两个孩子,孩子们对妈妈说,妈妈我要喝水。
妈妈说好,分别给孩子倒了一杯水。
学校里有两个学生,学生们对老师说,老师我借书,
老师说好,分别给孩子一张借书卡。
孩子 = 学生 = 用户
妈妈 = 老师 = 程序员
水 = 书 = 用户需求 = [用户对领域的主观认识,领域模型之一]
杯子 = 借书卡 = 领域模型之二
领域模型要捕捉和包容的是用户需求,而不是用户本身。用户需求是用户对客观领域的素描,所以也是领域中的模型,我们(程序员)可以借鉴其宝贵的经验和认知,将水或书作为领域模型之一。
为了身体健康,妈妈对两个孩子说,以后喝水只能用自己的杯子,不能用别人的杯子。
为了便于管理,老师对两个学生说,以后借书只能用自己的借书卡,不能用别人的借书卡。
这样每个孩子只能使用自己的杯子,杯子有了特定的使用者;每个学生只能使用自己的借书卡,借书卡有了特定的使用者。
过年到了,两个孩子对妈妈说,妈妈,我们长大了,想喝酒,妈妈说好,使用你们的杯子盛酒吧。
可是拿杯子的时候,不小心把杯子摔坏了,于是妈妈给孩子们拿出新的杯子,这种杯子的类型可能与之前完全不同。
信息电子化时代到了,两个学生对老师说,老师纸质书太重了,想看电子书,老师说好,使用你们的借书卡借电子书吧。
可是借书的时候,找不到借书卡了,于是老师给了学生们两张新的借书卡,这种借书卡的形式可能与之前完全不同(或者采用最原始的笔和纸的记录方式)。
多年之后。。。。。。。
两个孩子去外地读书了,两个杯子,别的人还可以用来喝水,但妈妈可将其珍藏起来。
两个学生毕业去工作了,两张借书卡,别的学生还可使用,只不过老师将两张卡注销了。
用户有了新的需求:酒和电子书, 这是对领域新生事物的素描,是新的领域模型,我们为用户增添新的模型就可以了(对扩展开放)。领域中的事物更新换代,杯子变了,借书卡的形式也变了,但盛水和借书的功能是不变的(对修改封闭),我们用新的模型替换旧的模型就可以了。
与现实稍微不同的是,在代码中,我们可能需要事先预测未来可能的变化,在一些地方应用设计模式去包容变化,将来改动代码的地方降低到最少。
不过,这里包容“借电子书的需求”,比较简单,不需要设计模式,只要在BookDetail中增加一个字段,区分“纸质书”与“电子书”就可以了。
无墨之韵,方能韵味无穷;无为之治,方能应需而变。领域模型只有将用户置于模型之外,方能包容用户参差多态之需求。建立领域模型,是“有之以为利”,包容用户需求,是“无之以为用”呀。设计模式的精髓其实亦然。
大概可以分为三种观点:
1)用户在系统中存在镜像,是重要的领域模型,甚至是核心的领域模型(从张逸先生的设计UML图可以看出, 各位基本上与其一致)。
2)用户在系统中存在镜像,但不是(在此案例中)不是核心领域模型。(banq)。
3)我的观点是用户应在领域之外,在用户进入领域之中前,并没有用户。用户进入领域后激活了(担当或使用)某些角色,参与完成了某些活动。
完成活动的技术实现方式可以有多种,直接或显式完成,比如我写的代码;或者间接或隐式完成,比如通过事件驱动的风格;也可以采用其他风格,比如直接调用与基于事件集成的风格互补的方式。我在Jdon.com看到的对领域事件的讨论,感觉与其说是领域事件,还不如说是持久化事件。领域事件也许可以地分解为或对应为持久化事件,但多少有点技术驱动,而不是领域驱动。
我在学校接触过两种借书方式,一种是刷卡的,学校图书官使用的方式,可借的书较丰富,超期有罚款;一种是人工记录的(没有借书卡),学院藏书室使用的方式,可借的书较单一,没有时间限制,没有罚款。两者的借阅规则不同,可采用策略模式进行设计,但这里不说这个,说的是卡是什么?
从我的代码可以看出,卡上面记录了借书人、借书时间、归还时间、所借何书、借书数目。实际上,我接触的人工记录的借书方式,就是用白纸黑字来记录。我以为卡就是一种自动记录借书活动的方式,代替了笔和纸。
今天中午去银行取钱,看见一个同事在银行内排队取款,而我在外面的自动取款机取款,我想到了存折和银行卡都是记录取钱和存钱活动的一种实现方式。联想到,电视上看到一些讲述革命年代的电视剧,里头去银行存钱和取钱的方式,不是使用银行卡,使用的也是比较原始的笔和纸的记录方式,至于验证取款资格,可能要通过本人、亲笔信或其他信物等等。只是现在的银行卡通过密码的方式来样验证资格,并将这个资格审查的功能集中在卡上。
对用户进入领域资格的审查可以放在领域之外,也可以放在领域之内,这可能取决于领域安全的重要程度。
图书领域中需不需要一个图书馆管理员,之前有人跟我提过这点,当时我的回答是可以考虑将一部分借阅条款的功能分配给管理员,毕竟管理员是借阅条款的执行人。
但是,现在我觉得可以不需要管理员,但管理员的执行功能需要保留。为什么呢?还是从银行取款这事看,使用卡进行自动取款实际上没有营业员的,使用存折则需要。但两者之间有共同点,自动取款机和营业员都是充当执行银行业务规则的角色。我在代码实现中,将业务规则和业务规则执行人集中在BorrowingTerms,可以考虑将部分功能分配给业务规则执行人,但这里业务规则并不复杂,集中实现感觉也没有什么大问题,就我而言,在代码上感觉更紧凑一些。
商店可以没有销售员,就像自动售货机;银行可以没有业务员,就像自动取款机;图书馆可以没有管理员,如果也有一种自动的装置的话。这些只是形式的变化,他们扮演执行业务规则的角色是一致的。
说远一点,历史虽然是人的活动,但历史规律却又不以人的意志为转移,这多多少少看起来像一个悖论;经济活动也是人的活动,竟然也同样如此,市场经济比计划经济更有效,大概也是人放下了人自以为是的控制;刑事法律似乎为罪犯量身定做的,惩罚罪犯,可惜法律的意志似乎并不在于此,初听可能有点怪,法律惩罚的是犯罪行为,而不是罪犯本身,也就是说惩罚的是行为或具有某种行为的角色,而不是人。
上面涉及的一些解释已经超出我的知识范围,只能作为一种理解的思路,以供参考。你们所说的,我理解。我只是提供了另一种视角来审视领域,试图以更客观的方法去接近领域的真相,如果这么看,似乎有点“无情”,有点令人“悲伤”,人,你在哪里?
SpeedVan:
感谢Jdon007的祝愿,同时也对各道友送上新年祝福。
以下是三个问题:
一、
回到帖子主体,我觉得banq和Jdon007都和我说的不是同一样东西,为了避免出现误解,我先说出可能有误解的地方。
现实生活中的用户(或者应用层的用户),我称为驱动者(应用驱动者,执行者也可,应用执行者也可)。而模型中的实体,我称为参与者(业务参与者,或者场景参与者)。
我觉得,banq和Jdon007一直说不要加入驱动者,而我说的是参与者,参与者可以扩展到作为任何聚合根的称谓。因为我认为的参与者是在领域内的,所以排除人就奇怪了。有待banq和Jdon007验证是否是这种情况。
二、
另外一个问题,Context场景到底是在领域内,还是领域外的。我一直是认为是领域内的,场景是领域的划分,根据的是DDD中所指的上下文。banq认为是领域外,到底是如何理解呢?
三、
最后一个问题,卡还是人?在我的领域模型中是没有卡的,只有人,卡放到应用层作为认证的。看到“内存我在借书”=“内存中的我和书以相关身份进入借书场景”。现实中的我与内存中的我,不是同一个,所以说内存中的Reader("我")是现实中的我的镜像,映射。所以也由此引出Jdon007的模型中描述的Card,是用户的映射。卡借书,犹如杯子喝水一样奇怪,这样的模型,自然?为啥DDD中用Customer而不用其工具呢?Customer就是行为执行者,干嘛还要找个工具映射到Customer上呢?执行者拥有行为的执行权,工具拥有的是行为的效果(策略模式)。
而且在图书馆系统模型中,不应出现卡的,原因是,其是一个身份认证工具,并非执行借书的,和身份证一样,身份证对的是国家,借书卡对的是图书馆。
还有该注意的一点是,无论是“你”,还是“我”,还是B君C君,只要用的是A君的卡,那么在图书馆看来,使用者是A君,不会去理会真实使用者身份的。相当于一具受A君控制的行尸走肉——系统认为使用者就是A君,没有其他可能。
Liam:
在这里先祝各位道友新年快乐,万事如意!
我们首先确定领域范围:图书馆(不是学校)。
场景就是借书还书这个动作过程。
从Jdon007的代码来看,好像这个图书馆的借书还书过程是个无人监守的场景。
也就是说是不需要图书管理员的参与。
接下来陈述下我个人的观点。
我比较赞同SpeedVan的大部分观点。
重点是把用户看成是“图书馆”这个领域外的实体还是“图书馆”这个领域内的角色(Role)。
真实世界场景可以描述成:用户(学生或者老师)用借书卡在图书馆借还书。
转化成软件系统的领域中的场景后应该变成:登录用户(Reader)借还书。(现实世界的借书卡可以理解成这里的登录用户)
由此可见:
1.领域中的用户就是登录用户,是一种依赖领域的角色。是这个角色可以在图书馆中借书还书。
2.借书卡是现实世界中领域外用户(学生或者老师)用来登录图书管理系统并借还书的工具。虽然是领域中的Thing,但是由于有登录用户存在,登录用户(Reader)和借书卡的信息一样,所以借书卡可以从领域建模中去掉。
3.图书馆就是聚合根,借书条款(值对象)和属于图书馆的书(值对象)都是它的属性。
接下来的动作就是:
登录用户通过触发借书还书领域事件来驱使图书馆聚合根来执行借书还书这个动作(其实就是改变图书的状态)。
也就是说借还书这个操作是图书馆这个聚合根的行为,没有必要单独成为一个四色原型中MI。
欢迎讨论。
SpeedVan:
其实主要是对卡的理解,很多人认为卡中有很多信息,什么个人信息,什么余额,什么记录都在里面。其实不是的,为什么?要是我的卡丢了,我在图书馆的信息就永远消失了?明显不是,用户信息都在系统的某个容器当中(结构体,对象,或者数据库等),而并非在卡中,卡内只存在一个卡号而已。而其与领域实体中的某个用户的唯一标识绑定而已。银行何尝不是这样呢?存折丢了,不等于交易记录消失,它只是一个帐号和一张纸而已,他的交易记录在系统的某个容器当中,而并非在这张纸上。我不打印就没记录?明显不是,可以把这理解为功能需求。
没有系统时代的纸张记录信息,相当与系统内容器记录信息,而并非卡,或存折,卡和存折等都是登录手段,相当与认人和认信物一样。理解这一点的话,应该对各种交互系统都能了解了。
Jdon007:
我理解的“卡”是记录活动的一种方式,不妨想象为“纸”。卡是原始的“借条”或“欠条”的进化形式。卡里头存储的信息,我们一般都看不到,所以对其只是凭自己的认知进行判断,各言其是,都有可能出现错误。但是存折我们是看得到的,银行存折上打印的每一行上记录着什么时间,存取了多少钱。
银行系统有各种交易记录的备份,平常的欠条也是一式两份的,一份在用户手中(比如存折或卡等形式),一份在银行手中(比如数据库中的表或财务室的账本等形式)。
不妨想象两个场景:
1)银行被原子弹炸毁了,也就说银行内所有的记录都消失了。那么,每个人在银行内存的的钱,就白白没了吗?我想谁都不会乐意,当然贷款的就开心了。只要我们手上有符合要求的记录,比如卡和存折,记录着银行欠了我们多少钱,相当于民间借钱时打的欠条。我们是可以找银行要回钱的。
2)存折或卡丢了,我们手上的记录丢了。可是欠条一式两份,银行系统上面还有同样的记录,我们通过身份证等证件,可以找回记录,补办卡或存折。人们之所以愿意往银行存钱或者说借钱给银行,这可以说是很重要的原因之一,不像生活中,欠条丢了,打官司都可能要不回钱。
同样,借书也如此,借者(借书人)要记录自己借了多少书,被借者(图书馆)也要记录自己出借了多少书。借书人每次借书都要打借条,卡可以理解为可复用和自动记录的借条,节约了纸张和手写记录的时间。
“借书人”使用“领域”或“借书人”进入“领域”时,参与了“借书活动”,其中,活动规则,活动的各种工具(比如卡和存折)都是“领域”所规定的。“借书人”离开“领域”时,这些规则以及对这些工具的功能都是领域事先决定好的。“借书人”,使用这些工具,遵循领域的活动规则或规律,完成借书的活动过程。无论“借书人”离开“领域”,还是进入“领域”,无论“借书人”是否存在,这些规则、这些工具的功能都是“领域”决定的。
张逸先生把一些业务规则集中或映射于“借书人”身上,认为系统之中存在用户之“镜像”。这样,业务规则变化时,“借书人”的职责可能也要随之变化。而我将“借书人”置于“领域”之外,业务规则发生变化,是领域本身的事情,“领域的变化”与“借书人”可以说是无关的,尽管“借书”时,存在着“借书人”的角色。
借书人使用卡借书,饮水者使用水杯来盛水,至于借书人怎么看书,饮水者怎么喝水,这是他们自己的自由。
领域中只有书和借书卡,只有水和水杯,不同的借书卡借的书数量可能不同,不同的水杯能盛的水也可能不同。卡可以有唯一的使用者(借书人),水杯也可以有唯一的使用者, 使用者是否有资格使用卡或者杯子,都是领域规则所决定的。至于卡的形式,水杯的形式,怎么发展,怎么变化,这也是领域决定的。
领域中如果出现了新生事物,其发展也是自身规律在起作用。使用者可以引导或促进领域的发展,进入领域时要遵循领域的自身规律。
我可能忽略了一个细节,银行卡与借书卡存储的信息应该是不同的。因为卡里头存储的信息,我们一般都看不到,只能从使用方式来推断。取钱时必须每次刷银行卡(饭卡也是如此),但借书卡只需要刷一次就可以借多本书。
“银行卡”是“欠条”,记录银行欠了我们多少钱,(饭卡也如此);“借书卡”是“借条”,记录我们像图书馆借了多少书。“借条”与“欠条”记录的信息应该是一致的,“借条”放在“借方”手里,“欠条”放在“被借方”手里。
对于“借方”来说,丢了“借条”于自己来说并无利益损失,也就是说“借条”本身可以不包含任何借书记录。师生们手中的图书卡似乎就是如此。但是在“被借方”图书馆里头有每个借方“完整的借书记录”—即“欠条”,“欠条”与“借条”记录的信息本应是一致的,只是因为角度不同(一个从被借方,一个从借方),说法不同而已。
我代码中的卡与师生手中的卡是不同的。
代码中的卡是从“被借方”的角度记录借书活动的一种方法,是“欠条”,师生手中的卡是从“借方”的角度记录借书活动的一种方法,因为丢了借书记录于“借方”并无利益损失,所以师生手中的卡可以不记录借书活动的信息。
“欠条”记录的信息进行持久化,保存在数据库中,相当于把每个师生的“欠条”保管起来。
稍微总结一下,如果“卡”是“借条”,可以不存储借书信息,因为记录丢失于“借方”并无利益损失;如果“卡”是“欠条”,要存储借书信息,因为记录丢失“被借方”将受到利益损失。如果“卡”既可以充当“借条”又可以充当“欠条”,则根据不同阶段分解为前面两种情景。
图书领域模型中的卡本质对应的是“欠条”,现实中借书人手中的卡对应的是“借条”,本来是一一对应的,可是从利益的角度考虑,“借条”可以不记录“借书信息”。
我发现在这个具体的例子我并没有做到完全遵循“无之用”的观点。将“卡”理解为“借条”而不是“欠条”,无意间又将用户包含进来了,尽管它们本来是一一对应的,在代码上也不需要修改。一个这么简单的例子,要贯彻一个观点都不容易。“无”有很多很多含义,这里有“不以人(用户)的意志为转移”之意(即无用户意志,这大概也是“无”的最核心的含义),用户进入领域要遵循其活动规律。
卡的含义,不是这里最核心的矛盾所在,我的观点是“领域独立于用户,不以用户意志为转移,不论用户是否进入领域,是否参与领域的活动”。
Banq:
不要纠结于图书卡了,图书卡实际是图书借阅活动的一个结果,也就是活动的结果。这里的“活动”可以表述为四色原型的MI,可以使用领域服务来;“结果”可以落实为存储的数据表数据。
在没有计算机软件系统时,大家就通过一个卡来替代数据表记录,就像财务手工帐簿一样,就像库存系统的入库单和出库单一样,如果你根据入库单和出库单去建模,那就走了数据表建模老路了,要认识到:实际领域中是没有图书卡或入出库单,这些都是人工记录的(起到类似数据表持久保存的作用),在有计算机介入情况下,这些就不需要了,如果你还将他们移植到软件中,就复杂化,我不知用什么来形容。
从四色原型重新看需求,很简单啊,“活动”当然需要角色人参与啊,四色原型四个,我感觉纠结的原因可能还在于数据库背景,在OO思维中,数据表就是对象拉的屎,是结果,不要把结果当原因,否则就容易纠结。
Jdon007:
呵呵,纠结,如果是原地踏步,当然没有啥意义。但SpeedVan等人的说法确实促进我进一步的思考。卡的原意是“欠条”,其命名可能需要修改。
张三向别人借一笔钱,借钱的过程中,张三要写一张欠条。欠条记录了这次借钱的一些信息。欠条不不等于张三,尽管欠条是张三写的。
我对卡的含义说了又说,主要是其他几位道友一致认为“卡是用户的映射”,而我认为不是。
图书领域中可以出现“借书人”这一角色,完成写“欠条”的职责,
也可以出现“管理员”这一角色,在参与“借书活动”时执行“借阅规则”。
我的代码中存在这样的问题,即“欠条”可以“自己写”,“借阅规则”可以“自己执行”。而事实,“欠条”写的“职责”应分配给“借书人”,“借阅规则”执行的“职责”应分配给“管理员”。
但是“欠条”(卡)本身是可以替“借书人”完成“写”欠条的过程的,“借阅规则”本身也可以替“管理员”执行。
也就说,这里又可以不需要“借书人”和“管理员”,因为“欠条”(卡)和“借阅规则”身兼二职,既担当“被执行的角色”,又担当“执行的角色”。如果两种角色分开设计,也可以,看情景而定。
现实中“卡”确实替“借书人”完成了“写”欠条的过程,所以在这个具体的案例中不妨把“借书人”这一角色去掉,如果还有其他职责非“借书人”来做不可,那么可以添加“借书人”这一角色,比如卡丢了,执行补办“卡”的职责。
角色不能简单地理解为用户的映射,尽管用户进入领域可以扮演某种角色。张三在公司担任经理,但张三不等于经理。
我并没有排除用户参与活动时扮演的角色,而是在领域中排除“张三”这个具体的人,甚至更多具体的人“李四、王五、赵六”等等。因为“领域的活动规律不以人的意志为转移”,即不以“领域的用户意志为转移”。
Banq:
讨论这个问题本身,我认为就没有真正理解我上贴对“卡”的定义,首先,“卡”是借阅活动的结果记录,是借阅这个MI(四色原型)拉的一泡屎,根本不就应该是讨论的重点,你们应该都被迷惑了,把结果当本质了。
而“卡是用户的映射”则更加极端,因为卡是借阅活动的静止结果,通常是数据表持久保存结果,所以在“卡”这个表中肯定有用户ID。这实际是在数据库思维上深入表现。
我的意思是,你们讨论方向可能完全走错了,还是清零回头重新再思考一下可能比较好,新年新开始吧。
SpeedVan:
“卡是用户的映射”这并不是我的观点,我的观点早已说明了:“领域中不存在卡”。甚至我认为存折上的记录只是一个View层的东西。
“卡是用户的映射”是我从Jdon007的代码中读出来的。因为他从“人使用卡”出发,认为是“卡”才是行为的执行者。
(我的习惯是用记事本写回复,然后复制过来提交的,忘记提交了-。-|||,以下是我之前想要说的:)
看来你,还是不了解我之前所讲的:内存领域中的实体和现实(或应用层的实体)不是同一个实体。这两者不是等价关系的。上面liam就已经说过“领域内的角色(Role)”。我从来也没说过,领域实体(参与者)只能对应一个现实实体(系统用户)。
应用驱动者与领域角色(场景参与者等)不是同一个。你完全可以认为内存领域中的实体“我”,不是我,它是一具行尸走肉,只不过它带着我的信息而已(从这里看,和你card很像),现实中我点击存钱,则系统产生存钱场景,内存领域中的“我”和相关实体进入场景,执行交互。现实中的我换一个帐号,则内存领域中出现另一个实体“我”。因为存在注册登录认证过程,所以可以称这样的一“我”为我的镜像。镜像的意思并没有现实中的我干什么,内存中的“我”干什么,而是我有什么,他也有什么,而镜像不像影子,镜像可以无限多,而影子只有一个(当然不是完全的,与领域相关的个人信息才会存在上面,这跟你的Card理解是一样的,但是我不叫Card,我叫他为Reader,一个领域角色,而且我的Card另有所属——应用层登录)。镜像,没人说过只有一个。
领域中有很多的参与者,但这些参与者并不等于用户。因为注册后领域中就出现“我”,所以叫镜像,多次注册,就会出现多个镜像,你去申请多张卡,就会出现多个“你”镜像。而领域看到的是镜像,而非你,或者说领域认为那就是“你”。领域中所发生的事与现实中的系统用户无关,只与领域中的角色相关。
若果你真要说人使用杯子喝水的方式去理解的话,翻译过来就是人使用卡去借书。于是Card.borrowBook(Book);则有Cup.drink(Water);突然有一天人不用杯子喝水,把嘴张着,直接在水龙头下面接水喝,那这是Tap.drink(Water);?!?!
比起这样,我觉得Reader.borrowBook(Book);Drinker.drink(Water);更加自然。加入场景思维的话,可表示为borrowBookContext(Reader,Book).borrowBook();(BorrowBookContext.borrowBook(Reader,Book);)。这样看起来多自然。若果需要考虑使用物而引起不同效果的话,则增加方法(Reader.borrowHistoryBook();Reader.borrowBook(HistoryBook);),或者引入策略(BorrowBookContext(Reader,Book).borrowBook(HistoryBookStrategy);BorrowBookContext.useStrategy(MathsBookStrategy).borrowBook(Reader,Book);,或者增加使用物实体进入场景,同时把策略放到使用物实体上(这样可以简化)。当然,使用Mixin的写法又不一样了。
人使用刀去杀人,杀人的是刀,人不在领域,不在场景中,所以有不在场证明成立,最后判决——无解。(我觉得,理解驱动者,和参与者就能很好解析这句话错误了。)驱动者可以理解为客观规律(或者“道”),或者上帝,他们促使(规定/要求)这样的场景发生,于是参与者人.杀(人);,当中为啥没“刀”,因为并非关注点。若果刀也需要关注的话,那么刀决定效果,而并非决定行为。(或者当作看电影也可以)
分清楚:“系统使用者”与“领域角色”(或者“场景参与者”)不是同一个东西。人使用卡,是使用系统,并非在谈领域,卡是依赖系统的,并非依赖领域的。若果把卡归到核心领域,那么这样的核心模型就只能适合使用卡的图书馆了。(当然现在大多数是这样)
关于图书馆聚合根,的确,在图书馆领域中,人的方方面面都是和图书馆在打交道。如买菜者是跟卖菜者交互。如JiveJdon的account跟forum(不过我个人意见认为account应该分离出passport和user,所以我之前有一篇帖子说过,user不应该存在account和password,因为这些不是领域业务,而是进入领域,一切开始之前的条件,这是题外话了)。
可以把这样的思维写成一个填词句子:( )与图书馆进行借还书行为。我填的是Reader,Jdon007填的是Card。我俯视领域,我看到Reader在和Library交互。而Jdon007看到的是Card在和Library交互,而我认为这样的Card模型是在拟人化。而非直接性拟人化也未尝不可。
Jdon007:
借书活动是这个图书馆领域的核心场景之一。领域模型必须能够清晰地描述这个核心场景。
()与图书馆进行借还书行为,这个说法,没有说明这个行为如何进行,其中的交互过程是如何实现。
“欠条”是活动的结果,“写欠条”是活动的过程。“写欠条”的参与者是“借书人”,但“欠条”既然可以自动化地替“借书人”这一角色完成“写”的过程,就可以不需要“借书人”,就像“借阅条款”的执行者“管理员”也可以没有,如果“借阅条款”也可以履行“执行条款”的职责的话。
卡是“借书活动”的记录,卡“借”书,实际含义,是卡“自动记录”借了什么书。Card.borrowBook(book),的含义是卡“替”借书人“填写”借书记录。杯子是“盛水”,而不是“喝水”,Cup.hold(water),人可以不需要杯子,用手或口盛水,然后喝。
现在有“杯子”替“人”完成了(用手或口)“盛水”的职责。
杯子是饮水者?杯子不过替饮水者盛水而已。卡是借书人?卡不过替借书人填写欠条而已。至于饮水,读书,这才是饮水者与借书人的真正职责或真正想做的事情,盛水与借书(填写欠条)是可以由其他角色代劳的。
借书人向图书馆借书必须填写“欠条”,“欠条”的形式可以是“纸”或“卡”或未来的进化形式,这是领域的发展决定。当然这里用“卡”命名“欠条”确实太“具体”了,不妥!也许BorrowingRecord
更能体现其意义,与BorrowingTerm呼应。BorrowingRecord可以自动记录,BorrowingTerm可以自动执行。
至于用户与角色,我们的定义可能不同。举例,比如企业管理系统的用户是“张三、李四、王五、赵六”等人,“角色”是“员工、经理、主管、总裁”等。碰巧的是,在图书馆里系统的用户,恰好只扮演领域中的“借书人”这一角色。如果用户还可以扮演其他角色,你会发现“角色”与“用户”不能简单地理解镜像之类的,当然可以解释为这是多对多的映射关系,但我认为,两者之间的相对独立的意义,比映射关系要重要的多,更值得强调。
我想谈到这里,彼此的观点应该清晰了,应该也可以达成一致了。也许如banq所言,讨论的方向可能错了。
SpeedVan:
注意,思考领域时,就别牵涉到领域外,系统用户可以扮演其他角色,这样的思考不属于领域的。在用户登录时,基础身份已被确定,是管理员就管理员,是业务员就业务员,是客户就客户。站在领域中心向四周看,看到的是业务员1,业务员2,客户1,客户2,但领域不会去思考谁在用业务员1,或者客户2,在领域中最基层的角色就是业务员,客户等,最基层的角色实体就是业务员1,客户2等,与我们现实用户无关。你用banq的帐号登录发表文章,那么领域当中就是发生banq(这是领域中的实体)发表文章一事,与“你”没有任何关系。领域认为就是banq发表文章,没有其他可能。而这个角色实体banq,就是板桥的一个镜像。只是这个镜像banq,受到Jdon007控制而已。
若果从细分的角度,你认为借书还需要写欠条,那么写欠条还需要拿纸拿笔。还有拿书呢?借书就是写欠条,不拿书?写欠条不是借书,而是欠条是借书的结果,banq都有说到,欠条是一个结果,其实欠条就如相当于日志。
这里就说到写欠条,写欠条是什么时候需要想到的呢?就是需要对借书进行细分之时——借书分为“检验身份合法性”check();,“写欠条”new Record();,“给书拿书”remove(); add();。而我们把这三项合并起来就是借书了。也就是一次借书所需要做的事情,但别把“写欠条”当作完整的借书。“盛水”和“写欠条”,已经不再是“喝水”和“借书”了。
Jdon007:
呵呵,我自始自终一直在强调“无之用”,“领域独立于用户”,可以看看上面的帖子,几乎每个帖子,都在反复强调这点。是你们一直在强调什么映射、镜像之类的。
领域模型是对客观领域的素描,描述客观存在。模型中不可能出现,现实中“物理上”的借书过程,领域模型的本质可以说是一种“语言”,该“语言”描述“现实”,显然不是“现实”本身。“欠条”本身既是结果,也从“借书人”这一角色的角度描述了“借书”之一场景。
从“借书人”这一角色的角度看,借书过程若要细化,可以分为:
1)“图书馆”愿意“借书”给你。相当于你拿到了卡。
2)拿一张用来记录的白纸。相当于卡本身。
3)你写下欠条,把书拿走。相当于你在卡上写下记录,但卡可以替你写,自动记录。
除了第1)点放在应用层实现,相当于用户进入领域的资格审查,如果很重要,可以考虑移入到领域中。至于其他两个过程在MI代码中已经体现了。“借书过程”的“给书”这一动作或职责是“图书馆”做的事情,与“借书人”这一角色无关,这在MI代码中也有体现。
盛水喝 = 借书看
盛水 = 借书
喝水 = 看书
杯子是饮水者?杯子不过替饮水者盛水而已。卡是借书人?卡不过替借书人借书而已。至于饮水,读书,这才是饮水者与借书人的真正职责或真正想做的事情,盛水与借书是可以由其他角色代劳的。
SpeedVan:
晕了,“领域独立于用户”我没有反对过吧?
这里的图书馆的系统,是借还书系统吧,哪来的看书?
从你的回复我看出我们思维的差别在什么地方了,我从一开始压根儿就没有把读书包括进来,借还就是领域的基本业务,而不是业务的方式(借来读)。
我理解的三个“=”应该是下面这样:
盛水喝 = 拿书借
盛水 = 拿书
喝水 = 借书
人想喝水,是以喝为核心的行为,人想借书,是以借为核心的行为。人“是否想借”与“是否想看”是无关的,借还系统与物品使用何关呢?借还系统和阅读系统的差别。
摘录
家里有两个孩子,孩子们对妈妈说,妈妈我要喝水。
妈妈说好,分别给孩子倒了一杯水。
学校里有两个学生,学生们对老师说,老师我借书,
老师说好,分别给孩子一张借书卡。
光这句就带有误导性质了,我要喝水,妈倒水给我,我要借书,是老师拿书给我。若果引入借书卡,则应该是:我要喝水,妈给我杯,我要借书,老师给我借书卡。第一句是关注行为与行为结果,第二句是关注行为与行为方式。
2011年02月13日 19:31 "Jdon007"的言论
从“借书人”这一角色的角度看,借书过程若要细化,可以分为:
1)“图书馆”愿意“借书”给你。相当于你拿到了卡。
2)拿一张用来记录的白纸。相当于卡本身。
3)你写下欠条,把书拿走。相当于你在卡上写下记录,但卡可以替你写,自动记录。 ...
这三点我也来说说:
1)图书馆愿意借书给我,相当我拿了卡,也就是说卡,代表的是愿意,也就是一种身份认证,权限认证。
2)拿一张用来记录的白纸。相当于卡本身。这句我不认同,卡不是那张白纸,那张白纸是在系统内部。因为卡中的Record不能决定系统任何数据,卡只是单存的View,卡内的Record只能当为单纯的结果,而非实体Record,或者说并非“借”这一事件。数据事实只能依据一个地方,除非卡内信息能跟系统同步,而且保密。银行所用柜员机,是跟银行系统同步的,别以为它是根据你的卡内有多少钱来进行业务的。
3)我写下欠条,是在系统中出现欠条Record,而并非保存到卡上,若果卡上真有Record信息,那么它就相当于RecordForm,这是结果展示,而且申请卡上的协议一定写着:“一切以系统为准”,不相信系统咋办?在银行上,银行会出示纸张证明和监控记录,若果还不信,则是以违背协议为根据,一概认为无理取闹。银行本身就是信用机构。系统的关键信息从卡中读取本身就是一个滑稽的做法(除帐号,卡号外)。学校的饭卡?请看清楚你打卡的机器所连着的网线(不一定是相交线,更多的是机器专用的线)。这就是物联网。
把金钱余额存在卡上的做法并非没有,这要看风险问题。至少在我看来图书馆的书是其命根,银行的钱是其命运,他们不会把命根和命运放到用户手上。试想若果卡余额决定购买,若果卡的余额大于系统余额,呵呵,到时候谁亏了呢?
在这里我补充下,“镜像”一词的说法依据,镜像一词跟“领域模型是对客观领域的素描,描述客观存在。”是同一层次的,是模型跟客观的一种关系描述,我们想到的有客户,管理员,这些思考很直接,是模型,但在思考时,往往我们是先思考实体的,纯技术角度说,实体对应的是对象,但朴素的角度说实体对应的是“镜像”。客户是领域里的一个模型,那么从客户模型得到的客户1是现实客户1的什么呢?我称之为镜像(其实就是模型,更为通俗的说法而已,若果叫模型的话那么,客户就变成模型的模型)。从模型中得到的永远不是现实,但他们之间肯定有一种关系。
Jdon007:
主要的观点,如果可以理解,就不纠结了。
1)用户不是角色,可以扮演角色,用户与角色的关系可以理解为多对多的映射,但我认为其相对独立的意义更重要,更值得强调。
2)领域中的其它角色,如果可以替用户进入领域将扮演的角色完成其职责,就可以将用户所扮演的这一角色从领域中去掉;如果不能则保留用户进入领域时将扮演的角色。
邮箱替用户收发邮件,杯子替用户盛水,借书卡替借书人书写借书记录,自动取款机替业务员执行业务规则。只要不是用户最根本的需求,用户进入的领域的角色都可以由其它角色代劳。比如喝水、吃饭、看书,至于盛水、买饭或做饭、借书或买书,都是可以由其它角色代劳,
或者说使用其它角色来完成职责。
其余的细节问题,比如交易涉及的利益关系、相似例子的类比,前面的帖子应都有所解释,如果没有,也算了。看帖的、顶贴的,如果有其它方面的高见不妨也说出来,确如banq所言,不再纠缠这个细节问题了。
SpeedVan:
2011年02月14日 21:34 "Jdon007"的言论
都是可以由其它角色代劳,
或者说使用其它角色来完成职责。 ...
是的,原来的观点差别是在这里,我们论点也扯远了。我之前就说到了,其他角色来代劳,即是我之前所说到的拟人化,也未尝不可(Card与Reader是同一个概念——行为执行着)。只不过,我认为更直接地认为是客户,借书人这样的参与者,可以避免因为“使用”所带来的绑定。客户,借书人这些角色是领域中肯定存在的,而使用的东西缩小了模型的适用范围。缩小范围也没错啊,因为它一样能够准确表达该系统模型。