对领域驱动设计的初步认识(五)

    一边看着Evans的书,一边学习jivejdon源码,同时印证着我的经验和想法后有这样一种感觉:DDD就像一颗闪光的明珠,但从目前来讲还不够完美。
    这不禁让我想到一个问题:随着各种新的建模技术出现,我们应该如何去把握?其实在论坛里也看到大家有时也会有很多迷惑,对各种建模技术名词不知道该如何去正确的区分和运用。当然,我自己也有。为了解决这个问题,我们应该知道真正的好的建模应该是个什么样子,以此来验证各种建模技术是否能够达到心中的目标。这就好像那些玄幻小说一样,每一本玄幻都有一套体系的设定,最终的境界都是一个“道”字,都会有明确的境界标准。
    那对于我们做企业业务建模,终极目标应该是个什么样子呢?我认为这个终极目标一定是非常复杂的(也就是我原来常说的大项目场景),因为只有在复杂的场景下才能真正检验各种建模技术的偏颇。想想看,我们的建模目标应该向谁学习。论坛也有人说过,“自然的就是最好的”。是啊,经历过亿万年才进化出来的模型难道不值得我们学习吗,难道不是我们的目标模型吗!呵呵,答案已经呼之欲出了,就是“仿生物建模”。是的,没错,如果说利用我们的建模技术能够去构建出一个复杂的仿真人,具备人的一些特征和功能的话,那这种建模技术就是完美的。记得前面的帖子里,我总是提到大项目建模,有人认同,有人不以为然,还有人茫然。其实离我们最近的最熟悉的大项目就是“人体”,我们可以学着用学到的建模技术去“对人体建模”,去检验你的建模技术,这样我们会少一些迷茫。其实对于像DDD、EDA、DCI、CQRS等这些新技术,你都可以在人体奥秘中找到影子,可以通过人体本身的模型机制去验证这些技术的完备性,从而学会在实践中应该如何正确地去运用这些技术,另一方面也为这些技术的发展指明了方向。
    对我前面几个帖子有一个总结:从层次上来讲,企业的业务建模可以分为两个层面,”宏观建模”和“微观建模”。“宏观建模”是指首先要对企业做一个整体的信息化规划,对企业进行整体的的业务架构建模,其成果就是业务组件。其中的方法论可以参照IBM的CBM,不过IBM好像也只是咨询,真正的落地还要靠自己对CBM的领悟。
    Evans的DDD主要属于“微观建模”部分。对于“微观建模”,我认为分为2个方面:“结构化建模”和“行为建模”,这是一体两面。我觉得Evans对DDD总结了几个关键的要素:实体、值对象、聚合、工厂和存储,但其中还少了一个非常重要和关键的要素:“事件”。虽然banq在JF中引入了事件的设计,但我感觉还不够彻底。众所周知,人体是由很多细胞构成的,那细胞之间是如何作用的呢,其实就是“刺激”和“响应”。其中“刺激”就是“事件”,所以“事件”是业务模型本来就应该具备的要素,而不是什么技术层面的东西。从这点来看,JiveJdon中的用户发帖这个动作激发一个“发帖事件”,这个事件会导致一系列的对象和值对象的状态变化(关于jivejdon的疑问我会在后面的帖子详谈)。同时,我记得论坛里有道友对职责的划分产生疑问。那首先要明白什么是“职责”,从“事件”角度看,“职责的本质就是事件的响应”。这也就是为什么我说jivejdon对事件的运用做的不彻底的原因了。
    “结构化建模”是指建模中除了静态的实体和值对象的结构关系外,从“事件”角度看,实体或者值对象还具备一些“本能的反应”,比如"手指会弯曲"。而“行为建模”是指通过神经中枢(消息总线)来控制不同对象的本能反应来完成一个复杂的组合,比如"用手弹钢琴"。
    呵呵,先说这么多,下面的帖子我会结合jivejdon谈谈自己的疑问和想法。
[该贴被flyzb于2010-11-14 10:02修改过]

2010年11月14日 01:03 "flyzb"的内容
那首先要明白什么是“职责”,从“事件”角度看,“职责的本质就是事件的响应”。这也就是为什么我说JiveJdon对事件的运用做的不彻底的原因了。 ...

嗯,其实在这里面临架构方向选择,目前jivejdon是我对事件方向的一个尝试,在另外一个方向上还有DCI(Data Context Interaction),关于关于职责 事件 DCI,如何有机结合实现,还需要继续在理论上探讨一下,哪个可能是未来趋势。

用事件来实现职责,在过去SOA以及传统架构比较常见,也有Scala的邮箱通讯机制,如果说事件机制类似观察者模式,是一种分布式通讯;那么DCI则类似Mediator模式,它是一种封装通讯,其实比Mediator更彻底,根本就没有事件概念,直接和角色 职责 场景等业务建模概念结合。

事件的导入是否对非技术人员等多一个概念,事件到底是不是技术概念,这些还需要多多探讨,愿听其详。

    banq说的没错,这确实是jdon架构方向的问题。我想,所谓“业务建模”本来就是对客观世界的抽象,同样也应该遵循客观世界的基本规律。我们复习下文一下:
    相互作用原则全面、深刻地揭示了事物之间的因果联系,是因果关系在逻辑上的充分展开。在客观世界的普遍联系链条中,原因和结果经常互移其位、相互转化。受原因作用的事物在发生变化的同时也反作用于原因,从而把因果性关系转变为相互作用的关系。其中每一方都作为另一方的原因并同时又作为对立面的反作用的结果表现出来。整个物质世界就是各种物质存在普遍相互作用的统一整体,相互作用是事物的真正的终极原因,在它之外没有也不可能有使它运动和发展的原因。相互作用也是系统内部诸要素的关系和联系的形式。要素之间相互作用的方式构成系统存在的基础,系统中要素的相互作用是决定系统发展方向的因素。相互作用只有借助于特殊的物质载体才能实现,相互作用的内容取决于组成要素的物质层次和性质。例如,现代生物学把相互作用划分为分子的、细胞的、器官的、机体的、种的、生物圈等不同水平的形式。社会生活是最复杂的相互作用的形式。

    相互作用是客观的、普遍的。具体的相互作用是整个物质世界相互作用链条的环节和部分,相互作用的普遍性和绝对性通过无限多样的具体的相互作用而体现出来。相互作用是事物的属性、结构、规律存在和发展的条件。

    相互作用范畴具有重要的方法论意义。认识事物意味着认识它们的相互作用,要揭示事物的本质属性,就必须研究事物之间具体的相互作用的特殊性。相互作用的实质是矛盾以及矛盾诸方面的相互依存和斗争。在诸多因素的相互作用中,必有一种起着主导的决定的作用。在实际工作中,只有认清事物之间相互作用的特点和规律性,才能认识和把握事物的本质。
    “事件”这个词的确有技术范畴的嫌疑,但说到“相互作用”、“输入和输出”,大家都会明白的。我不知道banq有没有这样一种感觉:过去的OO过于僵化、教条,偏重于静态场景,忽略了客观世界运动和相互作用的本质规律,而最近出现新技术和对OO的反思“正在回归自然”。
[该贴被flyzb于2010-11-14 17:52修改过]

殊途同归呀。

在我发的《Hello, World! 我心中的道》帖子中,uda1341提到“得救之道”,这个使我联想到其它领域比如生存等,人们的终极思考竟然极其相似。《天道》(《遥远的救世主》)对得救之道的回答,竟然也是“道法自然”式的结论(神即道,道法自然,如来)。

在讨论中,我也认为“世界是有事物及其活动组成”或者“世界由事物及其相互作用组成”是非常朴素、直观的世界观。uda1341引用维特根斯坦“世界是事实组成的”观点,也解释的通,个人感觉也比较简单、易于理解,只是如何运用这个观点我还没有想清楚。还望uda1341能系统地整理自己的思考,给大家更多的启发。

我个人认为目前的“领域建模”可能大多只停留在描述或者仿生“领域的事物(what)及活动(how)”,可能并没有深入研究领域的模式或规律(why)。研究领域的模式或规律,也许也没有这个必要,因为我们只是在做工程;甚至也没有这个可能,研究规律谈何容易。这似乎可以看作工程与科学的一个边界。

举个例子,复杂网络的建模,复杂网络的具体例子有生物网络、交通网络、互联网、神经网络、社会网络等,科学家要从这些网络抽象一个通用的模型,这个模型要足够简单,又不能太简单(比如,只将网络当成点与边的集合),一旦成功建模后,科学家们便可以研究模型找出规律,用于指导实践和预测未来。可是构造一个通用的模型几乎不可能,建立起来的模型往往只能反映现实的部分特性。不幸中的万幸,是作为工程师,我们可以选择避开这个难点,很多时候也只能这么做。正是避开了这个难点,四色模型我才感觉很简单。

Object-Oriented,object这个术语在台湾被翻译为“物件”,这个词本来是可以接近“事物”这个概念,可是从这个术语被发明以来,其含义远没有勾勒出“事物”丰富的内涵(在我的帖子中提及了一些),此外这个“术语”也没有表达出程序领域自身的特性。对OO不满的人大有人在,比如stl的发明者对OO嗤之以鼻,linux之父也曾经炮轰C++,这与OO本身的表达能力的局限性不能说没有关系;OO的特性,在PO中模拟实现也不复杂。(在帖子中有个简单的例子以及相关谈论。)

相对PO,对OO的运用我更娴熟,最近的思考使我开始也意识到其不足到底在哪里,以前我只是思考过PO与OO的相通与相异之处,却从未思考过它们具体的缺陷在哪里,未来的语言会怎样解决这些问题?

还有一点,关于建模与需求分析的关系,之前我只是简单的认为是“需求分析的目标是就是建模,建模后指导技术实现”。最近在思考即时通信的领域模型,发现模型本身也有助于我们做好需求分析,如果有可能,甚至可以放在需求分析之前,或者对两者以拉锯式、互相推动的方式进行思考。
[该贴被jdon007于2010-11-14 20:01修改过]

2010年11月14日 17:46 "flyzb"的内容
相互作用范畴具有重要的方法论意义 ...

“相互作用”这是毋庸置疑的,关键是是如何表达它们?用事件 还是DCI?
这个问题比较纠结。呵呵。

    看来我们都有共识,“相互作用是关键”。同时,我们也认识到,“事物之间相互作用非常复杂”。我觉得DCI所关注的核心——“场景”是事物行为的表现,但不是客观的存在。我们还是以“人体”为例,试想一下,“打一套太极拳,做个广播体操,360度空翻。。。”都是复杂的场景。如果把人体能够做的各种“运动场景”加起来,会可能出现“场景爆炸”。当然,这些复杂场景里存在大量的重复的子场景,但这些子场景在数量上仍然是爆炸性的。所以我建议学习“经过亿万年进化而来的客观存在的生物控制模型”,我们都知道“人体是通过神经网络来控制不同的肌肉、骨骼来做出各种复杂的动作的”。“刺激和响应”是客观存在的,而“场景”只是我们对客观世界的主观认识。
[该贴被flyzb于2010-11-15 12:50修改过]

“刺激和响应”

举个例子,人的最外层的皮肤就如同“门面”,用来接受外界的“刺激”,内部再由“大脑”下达指令,指挥各个“肢体”进行“响应”。

“神经网络”是被这层皮肤覆盖加以保护。

“神经元”向“皮肤”注册可能完成的“响应”,而“皮肤”接受“刺激”后由大脑进行反馈而回调“神经元”,“神经元”再完成各个指令动作。

我记得在你的(三)中,有这样一段:在这些不同的业务场景下,user都可以聚合根,这很正常,因为人可以干不同的事情。但如果把这些业务集成起来就有问题了。所以说作为聚合根的领域对象应该是业务场景类,而不应该是类似user的这样的类

这里所说的业务场景类应该就是DCI实现合适的,不同的功能就对应不同的业务场景,你的系统有100功能,就有100业务场景。

这里的业务场景是软件运行的场景,而不是我们要专门定义个业务场景类,注意,这里要从被传统OO语言误导中走出来,谈到对象,就首先要建一个类,这不对的(相关讨论),我们这里业务场景实际是一个运行时动态混合对象,就类似MVC中Controller。

无论MVC的Controller 或Mediator模式,还是DCI模式,我们已经默认将他们划归为“行为模式”了,行为模式一种注重行为相互作用的大类别模式,其中有Command模式 观察者模式 等事件模式,而他们之间又分为两种类型了,一种事件模式;一种DCI模式(暂时这么分类)。

问题纠结在于:这两者到底各有什么优缺点?

[该贴被banq于2010-11-15 14:49修改过]

    其实,我并不认为DCI和事件是对立的,相反它们是从不同角度去描述对象行为,如下图所示:

场景和事件

    我认为“DCI就是在一定的场景下不同的角色(对象)在交互,同时相互传递数据”。DCI强调对象在场景下变成角色,并参与交互;DCI中的对象的行为是动态的。而我认为事件更揭示了DCI的本质,在场景的刺激下,一个对象发生了行为,然后产生后果(事件)激励着一个或者更多个对象发生行为并产生更多的后果;通过这种连锁反应(我称之为“事件路由”)表现为多个对象相互作用并传递数据(消息)——这就是“功能”。
    从上面的图片就可以看出,事件对交互的描述更加精确和灵活。然而更重要的一个难题:"如何在复杂交互场景中解决一致性的问题?"。比如经常会遇到这样的问题:“哎呀,场景1:对象A的行为会影响到B,再会影响到D,反过来影响到C;场景2:A——》B——》D,哦,晕了。。好像还有A——》C”,这其中可能会产生大量的漏项或错项,使得领域对象的一致性很难得到保证。因为现实世界里事物的交互太复杂了,尤其是在中国,尤其是要做中国的管理软件产品。在我的项目实战中发现,“事件”可以让我们在对象产生行为后只需发出事件即可,不用去管谁会关心这个事件。这个优点非常重要,可以让我们只关心对象行为本身;而事件的影响是由场景决定的,场景的复杂多变决定了“事件路由”的形成,也就是“业务规则”。
    呵呵,上面的分析让我更加坚信:“业务建模应该遵守客观规律”。
[该贴被flyzb于2010-11-15 23:09修改过]

2010年11月15日 22:58 "flyzb"的内容
我并不认为DCI和事件是对立的,相反它们是从不同角度去描述对象行为 ...

嗯,这句话很有启发,让我联想到Scala就是将异步事件消息和DCI结合起来,当我们编写第一Actor(相当于类)时,是使用DCI的方式加入这个Actor对应角色的行为职责;而Actor成为对象在运行时,Actor之间是通过异步消息事件(邮箱)通讯的。

DCI更侧重的是一种行为混合注射方式,是编程阶段一种模式,因此,将DCI归结为结构型模式可能更合适,就像MVC模式更多是一种结构型模式一样,MVC模式已死,而MVC中C是Mediator行为模式,DCI中I代表行为模式。

而事件就是一种完全运行状态名词,可以和“相互作用”直接对应。

重新想想前面我说jdonframework面临方向选择,也就是事件和DCI选择,为什么有这困惑,是不是Java这个语言不能象Scala那样,完美地将事件和DCI有机结合在一起呢?

总结一下:DCI概念更接近业务建模(业务建模代表客观事实),因此使用DCI可以让我们抛弃传统“类”概念,而直接以一种职责分配思维方式来编程,去除了业务建模和技术设计实现的鸿沟,也就是翻译转换过程;而业务建模中相互作用的设计,则可以通过事件或直接调用(DCI内部)来完成。

当然,事件分异步和同步,同步事件实际就是直接调用,这些都可以根据实体的聚合边界来确定,聚合体之间使用异步,最大可能松耦合;聚合体内部直接调用,一个聚合体类似Scala中的一个Actor。

感谢flyzb让我思考清楚这个问题,顿悟快乐很好,下面我想思考的是:如何在jdonframewok(jivejdon)将事件和DCI柔和在一起,反正也有纯粹的DCI API开源包了,也欢迎有兴趣者一起来推进Jdonframework重大架构发展。

    没错,DCI的确是和事件是融合的。比如我遇到过这样一种情况,对象A的一个行为可能产生2个不同的事件,而响应方是同一个对象B,具体走那一条路线取决于场景。从模式的角度看,DCI更强调分析,事件更强调实现。
    但同时要清醒地认识到,DCI是人对业务的主观认识,而事件才是真实的客观存在。不同场景之间可能存在着交叉或嵌套,就是同一场景也会充满着变化,容易出现对交互认识的遗漏或错误,这很难避免。这是一个客观规律,因为我们对事物的认识是需要过程的。    这需要在框架设计时,要考虑业务的柔性设计,可以很好的实现重构或组装。所以我希望“场景”最好是无形的,同时事件处理器的灵活和强壮。对于"事件处理器",要能够处理领域内部事件、领域间的事件、基于事件的分布式计算,以及能够和ESB的良好整合。

我一直有个疑问:事件网络是一个完整的整体,而DCI是对一个局部(场景)的封装。DCI如何在技术上保证不会破坏事件网络的完整性和可扩展性?
“事件”常被我做成一个“钩子”,可以根据场景的变化或者考虑新场景而加入新的事件响应或者事件路由。所以场景不能太僵化而封装一部分事件路由,因为一个场景中的一部分事件路由可能同样出现在其他场景中。
[该贴被flyzb于2010-11-16 23:27修改过]
[该贴被flyzb于2010-11-16 23:34修改过]
[该贴被flyzb于2010-11-16 23:35修改过]

2010年11月16日 17:18 "flyzb"的内容
事件网络是一个完整的整体,而DCI是对一个局部(场景)的封装。DCI如何在技术上保证不会破坏事件网络的完整性和可扩展性? ...

事实我们应该反过来想,那个整体是否由局部组成的呢?我们不可能一开始就扑捉一个完整具体的整体,即使在现实中,我们也是由局部开始的。客观抽象的整体可以说是不清楚的,我们是通过不断地对局部认识,然后慢慢组合成整体,于是我们就得到了具体整体。例如我们说到人,我们只能得到人这个抽象概念,可以说他是一个抽象整体,只有当我们认识了手,脚,头等,才可以组成一个具体的整体。可能有人会问,若果这个具体的整体跟客观的整体存在差异呢?那么我可能反问,既然我们只能得知具体的整体,而不能得知抽象整体是否如我们所想的那样,你咋能知道具体的整体跟抽象的整体不一样呢?或者说,人的认识永远是片面的,只有加入前提,才能成为全面的。

所以DCI角度上是基本没什么问题的,因为他符合人类的一种认识过程。而场景分割是否出现问题,是取决于对领域具体认识是否充分。DCI肯定会对客观整体的完整性和可扩展性造成破坏,这是不可避免的,这是由人的片面认识决定的。而对具体的整体的也会造成破坏,这是由认识的差异和是否充分等因数造成的。

对于“场景”无形,其实场景可以有形也可以无形,因为一个事件必定会引起有且只有一个场景(子场景可理解为再次分发事件),试想若果场景以事件来命名,那么有多少个事件就有多少个场景,这是有形的。但反过来,场景是没有名字的,只是一个载体,真正的行为选择取决于不是场景,而是事件,那么场景则是无形的。其实我们也可以从这里看出,DCI和事件驱动是殊途同归。

2010年11月16日 17:18 "flyzb"的内容
我一直有个疑问:事件网络是一个完整的整体,而DCI是对一个局部(场景)的封装 ...

这个问题是这样看的,如果把DCI看成程序代码前端,那么事件是结果,事件是程序代码运行的一种结果,因为事件触发源泉来自于用户输入,事件带有能量的意思,可以把事件看成中医上经络或气。

既然事件是程序运行的一种结果,那么我们可能在分析设计阶段不必太考虑结果事情,更多的是关注业务场景,这个业务场景需要哪些角色参与,这些参与的角色的原始身份也就是实体是什么;参与这个场景中这个角色履行什么动作行为。

我的意思是这样:软件分析设计阶段更多是DCI,而只有到技术架构实现阶段才会考虑到事件,可能会通过组件方式来切割,组件和事件都应该属于技术架构层面的。

2010年11月17日 11:35 "banq"的内容
软件分析设计阶段更多是DCI ...

总的来说的确是这样,当人们不断地用DCI来分析设计时,慢慢地就会对框架产生需求,让代码更体现思维,让代码更可维护。在技术上的DCI,可以说是事件的封装。不知这样说是否合理呢