业务模型统一描述

我认为,任何业务可以描述为:

1,时间,
2,场景(上下文),
3,角色(party),
4,主题(事件启动,事件源、动机),
5,行为(事件步骤,含中间状态),
6,结果(状态持久化)

结合DDD和CQRS,应该很好理解,也比较完整。

其中比较关注的是,面向主题的这个因素,这样可以让事件启动,独立于角色(party),边界划分更为清晰。

如果能用上述的6个方面进行描述,那么所谓的统一语言就可以统一了。

wangcity,2012-09-04

由,时间,参与者(角色),主题,可以确定唯一性

由中间状态可以控制无需时刻的一致性的事务。

将最终持久化看成是一个中间状态的特定时间点,统一看待。

不错,统一语言其实早就存在,只不过朴素的被动存在,比如数据库术语就是目前用得最广的统一语言,最后迫使业务人员也要学习数据库知识,关系数据库变成万能模板。

统一语言统一的是业务语言和技术语言,并能把他们的理解统一映射起来,否则技术人员觉得业务很难实现,业务人员也会在细节中迷失方向。

细节决定成败,也能让人迷失方向,包括通往成功的方向。

UML也是一种统一语言,BDD也是,包括倡导统一语言的ddd,它的实体和值对象也是一种统一语言,统一语言其实是统一思想。

统一语言是不是和DSL语言 Domain-Specific Languages有某种关系?
[该贴被banq于2012-09-05 10:11修改过]

一,两个世界
客观世界
主观世界

客观世界:是包含真实的业务和软件。
这是独立,客观存在的。
例如,没有任何软件,业务也是可以做的。写软件的人假如死了,软件也是客观存在的。假如比尔盖茨死了,windows依然存在。

主观世界:头脑中的世界,包括:业务人员的头脑中的业务模型,开发人员(架构师)脑中的需求模型(软件的模型)

因为软件并不是1:1的去复制出一个虚拟的业务,而是从业务中抽取出,过滤出一部分需要由软件支撑的部分来进行开发,所以,业务的范围大于软件的范围。

也即是说:

业务模型-》(抽取,过滤,设计,转换)-》需求模型

模型是主观世界的客观表达。

所以,业务模型,需求模型,就分别是商业分析师、业务人员和架构师、开发工程师们头脑中主观思维的表达,并通过模型进行记录,形成一个客观存在的表达形式。

正所谓,口说无凭,立字为据。

在从业务模型到需求模型的转换过程中

如果业务人员(客户和商业分析师)的表达,不够精确,不能传达100%完整的,正确的信息给架构师,那么,架构师的设计,肯定也是错误的。

之前,采用的例如,UML的用例图,BPMN商业流程图等,虽然可以在一定程度上达到效果,但是因为,UML的用例图对人的要求较高,所以很少有人能把用例图画对的。BPMN商业流程图,也容易出现,对流程,子流程,步骤,活动等判断不一致的地方。

同时,在软件基本上都是采用OO语言开发实现的,OO的本质,就是模拟客观世界。客观世界肯定采用的不是贫血模型。如果客观世界是贫血模型,那就得存在上帝,上帝指挥一切运转。

时间是连续的
时间是不可逆的

世界是异步的,同一时间,他在苦,你在笑
世界是并发的,统一时间,好多人在吃饭,好多人在睡觉
世界上事情的发生,都有其上下文,你在办公室工作,他在工厂上班
世界上事情的发生,都必有其原因,尼采说过,存在的都是有理的(不是合理,而是有理,哲学上的)

so,what?

2012-09-05 10:29 "@wangcity"的内容
之前,采用的例如,UML的用例图,BPMN商业流程图等,虽然可以在一定程度上达到效果 ...

这个很赞同,语言本身必须直观,不能太复杂,统一语言本为了直观统一大家认识,结果语言工具本身带来了问题,自相矛盾啊。

BDD的统一语言比较直观,模板是:
Given 什么场景
When 什么事情发生
then 然后怎样 是继续其他事情 还是中间状态,还是结果状态呢?

业务活动是随时间变化的一个连续过程,就象整个二叉树,只要截取抽象其中某个节点?

四色原型用四种颜色表达,比如什么角色在什么地方做了什么活动,产生了什么结果。

我个人感觉统一语言的元素不能超过五个,否则多了要让人去分辨这五个就很难,比如初学者对DDD中实体和值对象的区别开始很难去分辨理解,这样就妨碍了他们去广泛使用实体和值对象来标识业务。


UML,BPMN的最大问题是,当我画完后,业务分析人员怎么能够简单的向架构师证明,这就是对的?虽然UML,BPMN模型就在那里,但是怎么去证明这个建模建出来的东西就是对的?

缺乏可验证的方法和手段,而且,表达起来很困难,实际上,画的用例图,多数都是不精确,不正确,不可信的。

那么怎么样能够精确和可验证(简单的方法)。按照我第一个帖子里面说的,按照那些要素,用文字描述即可。这对于业务分析人员是很容易做到的,如果他能看懂福尔摩斯探案集,我想做到这个,毫无困难。

1,时间,
2,场景(上下文),
3,角色(party),
4,主题(事件启动,事件源、动机),
5,行为(事件步骤,含中间状态),
6,结果(状态持久化)

基于这种业务描述的表达,是很好写,很好读,很好验证的。

首先第一步,任何事情的发生,都不是毫无理由的(起码在业务领域是这样的),就像每个案件都会有动机,若没有动机的,那么不叫做案件,只是一个偶发事件。

案件,就是上面的“主题”

如果没有主题这个元素,那么一切就会回退到上帝,上帝让一切都动起来。。。
实际上,某个事情,是由于“主题”让其发生,让其动起来的。类似事件源的概念。

但是,同样的主题,会发生N次,例如,结婚,这个事情,会发生N次,全世界各地,很多场婚礼都在举办,而且,一个人还可以多次结婚。

但是,如果把“时间”,“角色(party)”这2个元素加进来,那么,只能在某个确定的时间,某个确定的角色(party),参与某个主题的活动。

换句话说,一个人,在一个时间,不能干两件事情。这是由时间的不可逆性,时间的连续性保障的。

那么就是,通俗的说,是一个人(party)在一个时间,干某个主题的事情,另外,是在某个上下文场景下来干这个事情。

这里面有个问题,就是“方法”,是放在这个party的内部,还是外部,或者说,我们用的是贫血模型,还是充血模型。以前,很不好区分,如果是贫血模型,那么需要一个“服务”的上帝之手,让这一切都动起来!而实际上,我们知道,客观世界的活动是由各个对象在一定的组织下,去各自完成的。

两个模型

第一个是,业务模型,是来自业务,业务是客观存在的,是会变化的。
业务模型,是真实业务的,主观化后的表达。

第二个是,需求模型(软件模型),即,后续开发出的软件应该是个什么样子的?记住,此刻还没有软件,软件是对照需求模型去开发的,就如建筑工人对照图纸去施工。

从需求模型到软件,中间会引入具体的技术支持,例如,某种语言,某种框架,某种中间件,数据库等。

从业务到业务模型,采用的是上面说的6个要素的描述。

从业务模型到需求模型,采用的是6要素到设计类的转换(人脑提炼),得到的是一个与技术(C++还是Java)无关的OO面向对象的表达结果。这个OO的面向对象的表达结果,是考虑如何构建对象--主要是对象行为(方法),而不仅仅是pojo,而是把该对象的业务操作,放进去。

这里关键的是,要考虑各个对象的相互之间的交互。

在贫血模型中,对象都是属性加上get,set方法,自身缺乏动态行为,仅仅充当一个数据存取的box,业务逻辑,往往放在服务层。

放在服务层,虽然可以,但是也有很多问题要解决,例如,分布式,并发,事务,性能等。

我们知道,客观世界,是不存在所谓性能问题的,例如,任意时刻,都有无数的原子在作着热运动,可以是海量的,客观世界不要求有多少的并发数限制。

而之所以并发会有并发量的限制,是因为对资源的占用互斥,如果一个主题事件的发生,不需要依赖于互斥资源,或者说资源是充分的,则就不存在并发的性能问题。

原子在其原子空间这个上下文作无规则的热运动,对象应该在其上下文中做自主的业务操作。

主题,是一个可以反复重用的业务操作模式,例如,对于结婚,这个主题。意味着,上门下聘礼,约定婚期,置办酒席,广发婚贴,选良辰吉日,接新娘,拜天地,入洞房等等。

参与者party,可以是不同的新人,一天可以举办无数场婚礼,只要其上下文不冲突即可。party在其上下文中进行着主题活动。

在这个主题活动中,有的party状态会发生改变,需要把这个状态持久化保存,例如,新娘从处女变成妇女了,新郎从未婚变成已婚了。或者业务活动,产生了新的对象,例如生了个孩子。

但是,最核心的,我们是关注一个主题活动,是否按照既有的流程模式完成,并不关心在某个时刻,对象所处的状态。如果对于结婚这个主题,我们定义为,生了孩子后,主题活动结束,则我们关注的是生了孩子没有,孩子是男是女,而在什么时候怀孕,在什么时候生产等,我们是不关心的。既,关注的是最终结果。

这就好象事务处理,我们也是只关注事务是否最终成功。

事务处理的核心,事务的检查

现在,我们都是通过2段式提交来做的,2段式提交,是基于一个技术实现模式的,而不是业务逻辑实现模式。

首先是协调者,发出预提交,如果参与者都回答yes,则协调者发出提交,然后开始正式提交(具体过程比这个复杂)。

这里有一个大问题,在所有的参与者都回答yes之前,那些已经回答了yes的参与者,必须等待一直到最后一个参与者回答yes后,才能进行真正的提交。

如果我们把结婚这个主题活动,当成是一个事务,那么直到新娘生完孩子后,迎亲车队的司机,才能算是完成了驾驶任务,这显然是不合理的!

如果通过数据库来完成事务,那么就会出现这个情况。

如果要是通过主题活动中,各个步骤的逻辑关系进行验证,那么就不会出现这种情况。一个事务中的某个参与者不需要等待所有的参与者都yes,才能进行提交,而只需要这个参与者的前提参与者yes了,就可以提交。例如,司机完成任务的前提条件是,客人都上了车,他就可以提交(开车走),到达目的地后,客人都下车了,他的任务就结束,而不需要等新娘生完孩子,才能提交他的任务。

那么实际上,事务的本质不再是针对数据库的操作,而是针对主题目的是否完成。

这样就把事务放到了一个大的,可以相互独立的,每个业务对象有各自提交前提条件,和提交后的,后置条件,从而进行切割,让事务中的每个参与者,都只依赖于它的前置条件,而不依赖于所有参与者是否都ready了,这样就可以提升效率,降低阻塞。

这里需要注意的是,这种事务,是基于业务逻辑的,而不是数据库或者技术机制的。

其好处是,可以从逻辑上回溯,是具体哪个业务对象,因为什么没有往下走。后面的业务对象,可以继续服务于其他的主题:例如,司机开车的前置条件是,所有人都上了车,如果不是所有人都上了车,那么司机在这个业务的上下文中,是无法继续往后走,于是,司机去服务于另外一场婚礼,在另外的那个上下文中,客人都上车了,于是司机可以往后进一步,开车送客。

这样在每个小的步骤上,业务对象是非事务操作的,无须等待。

同时,因为时间的不可逆性,可以对某个事情(时间,参与者party,主题,三者综合生成的“事情”)进行失败处理。

而不是把这些业务步骤简单的放入一个事务中。

并且一个事务的成功,之前是所有的子步骤都成功,才算是成功,这是因为,没有时间线索。

加入时间线索后,一个事务的成功,仅仅看最后一个子步骤是否成功,就可以判断了。例如,如果结婚这个主题的事情最终成功完成,只要看是否生了孩子,就可以判断。而不用关注中间的过程中的每个步骤。这是因为基于时间线索的传递性。

这里有一个新的概念,就是我们在具体编码过程中,要写些以前没有的东西,第一个是基于时间线索的,第二个是基于某个事情(时间,参与者party,主题,三者综合生成的“事情”)。

这里,某个事情(时间,参与者party,主题,三者综合生成的“事情”)不同于应用的上下文,“事情”表示的是业务范畴的。而不是程序生命周期范畴的。

“事情”表达的是业务活动的一个完整的生命周期,而不是一个运行在中间件上的程序的生命周期。

“事情”生命周期结束,表示的是“事情”完了,但是应用的生命周期还存在,这2者,是独立的。


统一的业务对象模型

我们知道在EJB中,分为有状态和无状态的bean,无状态的bean仅仅负责处理,他本身并不带状态,所以也无须持久化。

但是,客观现实中,没有这样的对象。例如,上面例子中的司机,虽然对于结婚这个主题事情,他无须记录他在拉了一车人前,或拉了一车人后的状态,但是,实际上,他的状态还是发生了变化。例如,年龄增加了,开车经验增加了,拉完一次后,收入增加了等等。

换句话说,任何一个对象,都是有状态,并且有状态改变的,也具有行为。不存在无状态的对象!之所以,EJB搞了一个无状态的bean,其实是因为无状态bean的状态,持久化回数据库后,这些状态对于业务的结果,不造成影响,例如,对于结婚来说,最终业务结果,是关注那个生下来的孩子,对于司机是否拉了一车人,并不关注。但实际上,司机的状态,是改变了。

所以,统一的业务对象模型,应该符合实际的情况,都统一到有状态上。

有人会说,司机的状态改变,对于我们整个主题事情来说,毫无意义,把他搞成一个无状态的就行了,为什么要去管他的状态。

这是因为,我们要记录完整的,基于时间线索的,主题事情,中间的环节需要能回溯。

当某个环节出现问题时,我们能够知道,问题是出在哪里,例如,婚礼没成功举行,是因为司机没有把客人送到。

统一的编程模型(有状态的),会简化开发的难度,并且会天然的具有可跟踪性,可回溯重建整个主题事件过程。具备事务的特性,可验证。

2012-09-06 02:04 "@wangcity"的内容
任何一个对象,都是有状态,并且有状态改变的,也具有行为。不存在无状态的对象 ...

非常好,这个我深有体会,实际中有状态的行为操作是最难的,比如用javascript写个Cookie,也属于有状态,写Cookie的方法孤单的挂在那里,谁都可以调试,测试时,有时突然发现有一个cookie被写,不知道被谁在什么地方调用了,追踪非常困难。

再举个例子:在一个功能行为中,有写数据表,然后再根据数据表的值判断,如果是第二次调用这个方法行为,就不用再写数据表了,结果这个功能行为只能使用在一个场景下,其他有类似场景的就不能调用这个方法行为,因为这个方法行为不是幂等的,更不是无状态的,两次调用导致的状态都不同,影响其他行为。

我的意思是:写入状态的行为方法可能很容易编写,但是状态被改变后,会引起其他行为的变化,打个比喻:最近一个人买了2000份肯德鸡,因为他发现肯德鸡员工操作不规范,提出批评对方不接受,这是一个状态,这个状态就导致这个人买2000份肯德鸡,有人认为是过激,我们知道一次过激行为又可能导致其他过激行为发生,所谓冤冤相报何时了。

这是一个写入行为导致的状态变化引起的连环反映,实际在编程中大量存在这种环环相扣。所以,如果你只重视行为,不重视状态,你第一次行为可以正常编写,其他行为是否正常就不是由你来控制的了。

所以,我有点怀疑,Scala的面向函数语言是否是鸡肋?因为面向函数语言侧重无状态,可是现实中无状态的案例太少,除非纯粹的数学计算和科学实验数据计算。

从逻辑上说:问题关键是很多业务无法从一开始区分有状态和无状态,如果是无状态,编程非常容易,几乎不需要测试;而有状态的应用的测试用例都很难编写,我想:如果我能想清楚测试用例怎么写,我实际已经能搞定这个有状态编程;而如果我不直接进行有状态编程,我无法意识到这是很有难度有状态应用,因为进行编程的过程是对有状态应用不断深入理解的过程。

[该贴被banq于2012-09-06 12:43修改过]