关于领域建模时考虑用户需求的出发点的理解

最近又重温了领域驱动设计的原著,有了一些新的理解。现在我觉得我能更好地理解jdon007之前说的下面这段话了。

“用户需求”不能等同于“用户”,捕捉“用户心中的模型”也不能等同于“以用户为核心设计领域模型”。 《老子》书中有个观点:有之以为利,无之以为用。在这里,有之利,即建立领域模型;无之用,即包容用户需求。举些例子,一个杯子要装满一杯水,我们在制作杯子时,制作的是空杯子,即要把水倒出来,之后才能装下水;再比如,一座房子要住人,我们在建造房子时,建造的房子是空的,唯有空的才能容纳人的居住。因此,建立领域模型时也要将用户置于模型之外,这样才能包容用户的需求。

所以,我的理解是:我们设计领域模型时不能以用户为中心为出发点去思考问题,不能老是想着用户会对系统做什么;而应该从一个客观的角度,根据用户需求挖掘出领域内的相关事物,思考这些事物的本质关联及其变化规律为出发点去思考问题。

下面以Eric Evans(DDD之父)在他的书中的一个货物运输系统为例子简单说明一下。

在经过一些用户需求讨论之后,在用户需求相对明朗之后,Eric这样描述领域模型:
1. 一个Cargo(货物)涉及多个Customer(客户,如托运人、收货人、付款人),每个Customer承担不同的角色;
2. Cargo的运送目标已指定,即Cargo有一个运送目标;
3. 由一系列满足Specification(规格)的Carrier Movement(运输动作)来完成运输目标;

从上面的描述我们可以看出,他完全没有从用户的角度去描述领域模型,而是以领域内的相关事物为出发点,考虑这些事物的本质关联及其变化规律的。比如第1点,如果是以用户为中心的话,就变成了:一个托运人可以托运货物,即具有托运货物的行为,一个收货人可以收货物,一个付款人需要付款;而他的描述中完全以货物为中心,把客户看成是货物在某个场景中可能会涉及到的关联“事物”,如货物会涉及多个客户,货物有一个确定的目标,货物会经过一系列列的运输动作到达目的地;其实,我觉得以用户为中心来思考领域模型的思维只是停留在需求的表面,而没有挖掘出真正的需求的本质;我们在做领域建模时需要努力挖掘用户需求的本质,这样才能真正实现用户需求;

这让我想到了我之前思考图书借阅系统时的出发点也是错误的,因为我之前总是在以用户为中心思考问题。比如借书者借书,借书者还书,管理员对书本入库,等等;经过上面的分析后,我觉得我应该以书本为核心出发点思考领域模型。如:
1. 一本书在某个时刻最多只能被一个人借,或者没有被借走留在图书馆;
2. 书本在被归还时需要检查归还时间,如果有超期则需要做罚款处理;
3. 一本书有库存信息,如书本存放位置,库存数量;
4. ……


大家觉得我的理解如何呢?
[该贴被tangxuehua于2011-10-03 18:09修改过]

2011年10月03日 18:09 "@tangxuehua"的内容
没有从用户的角度去描述领域模型,而是以领域内的相关事物为出发点。

看来认识识分析需求有两种线索,一个是以人为核心的需求分析;还有一个与人无关,事物领域自身的内在规律,或者称为“道”。

至于以哪个为主要为侧重点,还是要看人和事物在不同场景中扮演戏份的多少与否,比如在货运系统中,从这个名字我们也可以看出以货为主的一个运输系统,因此,人应该处于次要地位。

但是图书馆借书这个案例中,我个人认为好像不是以书为主的自主运动,而是以人为主,书为辅助的这样一个系统,如果以书为主,就发生“书被借”这样奇幻事情发生,呵呵。


写得不错,大概 @jdon007 没有看到,我使用前面加 at符合提醒他一下。



[该贴被banq于2011-10-10 12:00修改过]

人 != 用户

比如,人口档案管理系统,系统中应有记录人的信息或者说存在关于人的模型,但这个人的模型并不表示用户。

图书馆管理系统,用户群若是“读者”,那么应该将“读者”(用户)至于模型之外;如果用户群是图书馆管理员,那么“读者”(不再是用户,哪怕命名为用户),可以置于模型之内(比如要挖掘用户兴趣模型,发现什么的人群喜欢阅读什么样的书)。

物流系统,如果用户群是“顾客”,那么应该将“顾客”至于模型之外;如果用户群是“管理者”,那么可以将“顾客”(此时已不是系统的用户)放在模型之内。

将“用户”至于“模型”之外,用户群单一时,比较容易做到;若存在多种性质截然不同的用户群时,要注意到不同用户群将有不同的心智模型,尽管它们之间可能有交集。

但是,即便将不同性质的用户群的心智模型聚集在一起,也应遵循这个原则,并需要清醒地意识到视角潜在的变化(这可能被忽略或遗忘, 从而引起混淆)。

从整体上而言,“书”、“账号”、“书库”是模型(包括其结构/状态与功能/行为上的特征),可理解为系统的结构组成;“借阅规则”是场景规约或业务规则,可理解为系统的运作规则。

用户(读者)的心智模型不仅仅包含书,还应包含账号(图书卡)、书库(图书馆)等,模型有状态有行为,但其在参与场景进行交互时的受到场景规约的约束。

一种可以尝试的思路,是从业务规则(感知场景,从中提炼出的场景规约)中发现模型,并将业务规则与模型进行整体上的分离,让模型更自由,从而可能复用于不同的业务规则,或适应业务规则的变化。

至于模型的组织可以借鉴四色原型的PPT, 如图书馆的例子:Place(Library)、Party(Account)、Thing(Book), 或借鉴OO的message-based的思路,将模型分类为object+message或event+handler/dispatcher;当然,还有别的分类或组织模型的方法。

jdon007说的很多,我上面所说的人就是指用户,模型的使用者。我也强调的意思也是模型的用户,模型的使用者不应该被包含在模型内部。

2011年10月10日 22:07 "@tangxuehua"的内容
模型的使用者不应该被包含在模型内部。 ...

当你确定书是核心领域模型了,那么模型的使用者是不应该包含进来进行分析,通过借书这样一个事件或场景的发生,模型的使用者与模型发生了关系,这时模型书还是需要一个使用者的记录的。
模型:
Class Book{
String id;
String name;
}

用户操作借书事件激活借书服务,从而发生借书场景:
Class BookContext{

boolean borrow(Book book);

}

当borrow(Book book)事件被调用以后,拉了一泡屎,留下状态,这个书被借了,借阅者是某个用户,在数据表中我们可能有这样的记录:
book{
String id;
String borrowedUserId;
}

至于,这个borrowedUserId字段是否应该出现在Book模型中,则是另外一种设计考虑,比如我们认为borrowedUserId字段应该属于Book的状态,那么出现在BookState中比较合适,那么Book类有了一个新引用BookState:

Class Book{
String id;
String name;
BookState bookState;
}

Class BookState{
Book book;
User borrowedUser;
Date returnDate;//归还日期等
}

完毕。

我把整个过程列出来,是为了理清我们的思绪,不要让模型的状态去影响我们的分析。

以上这个分析的唯一前提是:假设这个系统是以书为核心,跟踪围绕书发生的各种事件。

所以,我们在描述时不能以“书被借”这样去描述需求,会产生误导,因为“借书”是一个事件,而“书被借”是一个状态,状态是事件的结果,事件是状态的因,而模型是事件的核心,没有模型,事件就无法发生,就象没有电话,就没有电话铃这个事件,没有书,就不会有借书这样的事件。

由以上倒退,我们知道,分析一个需求,核心模型是第一步,这一步是不应该考虑用户,前提是如果这个用户是在事件发生时介入的话;但是如果这个用户不是事件介入发生,而是属于模型一部分,如书的作者,那么就应该考虑用户。

希望以上思路对大家有用。

我的观点是:天地不仁,以万物为刍狗。

千万不要以为是人就扔掉:驱动者和参与者的区别。在领域当中的“人”,与“刍狗”等同,没有特殊地位。在整个领域世界中,应该全是逻辑描述,即使是人,也应该以简单对象来理解,构成事实离不开万事万物。

试想一个互动功能,交朋友,没有任何独立于人的实物,把人扔掉会是什么样的效果呢?

理解驱动者与参与者区别,就可以走出误区,account很容易让人想到使用者,分开Passport和Reader不就行了?Reader是人吗?是,但他完全和Book的地位一样,“刍狗”一只而已。

“以用户为中心”的思想主要来自于登录系统——认为系统事件参与者就是现实世界中的用户。虽然这样的观点在一定程度上可行,但这种关联只是一个潜在的认为而已——经过登录后,认为某个现实用户(或者电脑,或者仪器,或者IP)所发出的所有命令是系统中与这个登录所绑定的“人”发出的。这种关联是通过登录后获得,也就是说,若果没有登录,这两者应该是分离的,是两个独立的个体。



[该贴被SpeedVan于2011-10-12 13:56修改过]

公司这边网络限制得要死,上传老没反应,回家再补上图片说明吧



[该贴被SpeedVan于2011-10-12 13:54修改过]