业务模型统一描述
我认为,任何业务可以描述为:
1,时间,
2,场景(上下文),
3,角色(party),
4,主题(事件启动,事件源、动机),
5,行为(事件步骤,含中间状态),
6,结果(状态持久化)
结合DDD和CQRS,应该很好理解,也比较完整。
其中比较关注的是,面向主题的这个因素,这样可以让事件启动,独立于角色(party),边界划分更为清晰。
如果能用上述的6个方面进行描述,那么所谓的统一语言就可以统一了。
wangcity,2012-09-04
我认为,任何业务可以描述为:
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?
这个很赞同,语言本身必须直观,不能太复杂,统一语言本为了直观统一大家认识,结果语言工具本身带来了问题,自相矛盾啊。
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的状态,持久化回数据库后,这些状态对于业务的结果,不造成影响,例如,对于结婚来说,最终业务结果,是关注那个生下来的孩子,对于司机是否拉了一车人,并不关注。但实际上,司机的状态,是改变了。
所以,统一的业务对象模型,应该符合实际的情况,都统一到有状态上。
有人会说,司机的状态改变,对于我们整个主题事情来说,毫无意义,把他搞成一个无状态的就行了,为什么要去管他的状态。
这是因为,我们要记录完整的,基于时间线索的,主题事情,中间的环节需要能回溯。
当某个环节出现问题时,我们能够知道,问题是出在哪里,例如,婚礼没成功举行,是因为司机没有把客人送到。
统一的编程模型(有状态的),会简化开发的难度,并且会天然的具有可跟踪性,可回溯重建整个主题事件过程。具备事务的特性,可验证。
非常好,这个我深有体会,实际中有状态的行为操作是最难的,比如用javascript写个Cookie,也属于有状态,写Cookie的方法孤单的挂在那里,谁都可以调试,测试时,有时突然发现有一个cookie被写,不知道被谁在什么地方调用了,追踪非常困难。
再举个例子:在一个功能行为中,有写数据表,然后再根据数据表的值判断,如果是第二次调用这个方法行为,就不用再写数据表了,结果这个功能行为只能使用在一个场景下,其他有类似场景的就不能调用这个方法行为,因为这个方法行为不是幂等的,更不是无状态的,两次调用导致的状态都不同,影响其他行为。
我的意思是:写入状态的行为方法可能很容易编写,但是状态被改变后,会引起其他行为的变化,打个比喻:最近一个人买了2000份肯德鸡,因为他发现肯德鸡员工操作不规范,提出批评对方不接受,这是一个状态,这个状态就导致这个人买2000份肯德鸡,有人认为是过激,我们知道一次过激行为又可能导致其他过激行为发生,所谓冤冤相报何时了。
这是一个写入行为导致的状态变化引起的连环反映,实际在编程中大量存在这种环环相扣。所以,如果你只重视行为,不重视状态,你第一次行为可以正常编写,其他行为是否正常就不是由你来控制的了。
所以,我有点怀疑,Scala的面向函数语言是否是鸡肋?因为面向函数语言侧重无状态,可是现实中无状态的案例太少,除非纯粹的数学计算和科学实验数据计算。
从逻辑上说:问题关键是很多业务无法从一开始区分有状态和无状态,如果是无状态,编程非常容易,几乎不需要测试;而有状态的应用的测试用例都很难编写,我想:如果我能想清楚测试用例怎么写,我实际已经能搞定这个有状态编程;而如果我不直接进行有状态编程,我无法意识到这是很有难度有状态应用,因为进行编程的过程是对有状态应用不断深入理解的过程。
[该贴被banq于2012-09-06 12:43修改过]