李学斌:论应用间协作V3
Posted 蓝色的天空,朵朵的白云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了李学斌:论应用间协作V3相关的知识,希望对你有一定的参考价值。
说明
本文主要讨论了巨型复杂业务系统的一种构建思路,力图实现决策意志的快速、准确、一致的下传并简化实施成本提供实施效率。通过全业务领域的即时流程编排,实现全网业务IT系统的快速建设与迭代。本文所讲的方法其应用情景主要面向以业务流程为主的大型业务处理系统如电商系统。尤其适合业务繁杂、业务量巨大且运营灵活多变的应用情景。我们一般要花很长时间并投入很多财力才能打造出一个系统来满足这样的要求,并保持大量的人员来跟进运营的需要。这些系统一般会有数个或数十个乃至上百个应用群构成,具有庞大的层级结构。其中的每个应用都可能有自己的分布式部署以及在此之上的负载均衡,应用之间大都存在着远程调用。这些系统的迭代速度一般会很快,以求快速响应市场的变化及竞争的需要。为了方便后文描述,我把满足这种应用情景的系统称之为“复杂系统”。我承认这是一个糟糕的称呼,但我没有想到更合适且又简单的词语来描述它。但不管怎样单个应用或只有少数几个应用构成的“简单系统”本文并不适用。
文中还讨论了 “接口”相关的问题,请不要狭义的认为这是某一编程语言中的技术关键字,我们不妨可以将接口的概念适当的扩大一下,我们把应用间的各种数据交换行为都认为是通过接口来交换的,不管是通过MQ,RMI或者是Restful等等。本文还将接口做了进一步延伸,讨论了其与组织结构的关系。指出由于接口技术特性作用,促进了研发的职能型组织结构的形成。还指出复杂系统中的接口数量成倍增加了管理难度和研发运营成本等。本文力图从技术层面上给出了一种新的应用协作方式,来缩减系统的规模,提高系统的开发运营效率,顺带打破或弱化职能研发机构乃至缩减其规模。
本人从事于软件行业15年以上,在大型电商平台有6年的研发经验。亲身经历了由单一数据库构成的系统演变为现在日均近千万订单的“复杂系统”的成长过程。同时也目睹了研发组织机构分分合合,规模不断由小到大,以至于有些部门无法准确命名其职能含义的人员扩张过程。正是有了这些经历才有了本文的诞生的基础。
从认为“复杂系统”是个问题开始,我花了将近2年的业余时间来解决这个问题。之所以利用业余时间是有苦衷的。我提出这个想法的时候没有几个人明确认同我的观点,当然那时的想法没有现在这么完善也是其中一个原因,但我想他们更多的是不敢触碰太多的颠覆和未知的命运,并且这个想法还有很多困难和不确定性。同时在一个资本驱动的世界里,他们也不会投资一个机会渺茫又耗费财力的新鲜事物。基于这些原因我放弃了业余时间的几乎一切爱好把精力投入到这个想法的实现上。很高兴的是到目前为止其核心及Demo已经小有所成,于是整理此文以讲述其实现原理。
当然一个人的力量是有限的,希望大家能够认同我的想法,并和我一起来完善她。如果有什么想法和意见请和我联系,本人邮箱[email protected]。
定律及假设
在深入讨论之前我列举了几个定律和假设,除了“复杂度守恒定律”外,我没有找到类似的结论。我只希望我的假设是成立的,这将是我们后续讨论的基石。
复杂度守恒定律
复杂度守恒定律(Law of conservation of complexity)由Larry Tesler于1984年提出,也称泰斯勒定律(Tesler‘s Law)。根据复杂度守恒定律,每个应用程序都具有其内在的、无法简化的复杂度。
良好的设计只是将复杂度从一个地方转移到另外一个地方。机械化将劳动的复杂性转移到了机器中。而信息化则将更多的复杂性交给了系统。也就是说复杂度不会减少只会转移。OO(面向对象)设计的改良,从复杂性角度来看则是对复杂度的一种再平衡。算法的复杂度也只能在空间上时间上进行转换。
假设:业务形态认识决定系统形态
为什么同一个需求,不同的人会有不同的实现方式。这里面可能有很多原因,但起到主导作用和导致他们之间本质不同的是他们对要实现的东西的认识不同。最明显的例子就是,有些人做的很抽象,而有些人则做的比较形象。无论是抽象还是形象其实都是对需求的一种理解,是对业务形态认识的一种表达。
这种认识的不同直接决定了他们的设计具有不同的复杂度。请注意按照泰斯勒定律,它们的内在的不可去除的复杂度是相同的,因为他们描述的是同一种事物。之所以有不同的复杂度其原因在于外在可去除的复杂度。对于业务来讲,其业务逻辑是本身固有的复杂度,当我们用一个系统来实现它时,额外附加的技术复杂度,如存储、缓存、调用方式等等,这些都是外在的复杂度。一个系统设计的好坏就在于是否少的引入了“外在复杂度”。
我们无法回避“复杂系统”的业务复杂性,但我们可以在外在复杂度上找出路,我们大多数的技术工作确实也是这样做的。但我可以明确的告诉你这种想法是非常有局限性的!因为无法跳出技术范畴来来更宏观的看待整个事情。具体一点来讲,对于复杂系统来讲,技术优化只能体现在其中的每个应用中,而无法突破应用这个范畴。所以要想对现有复杂系统有可观的优化,前提是我们需要对业务形态有一种认识上的转变!
术语
除了上文提到的“复杂系统”外,这里还有一些术语被多次用到,为方便起见在此先解释一下。
组织
是一种简称,本文专指研发组织。
长流程
即传统所说的流程,用于描述一个任务或活动所需要的步骤或方法的有序集合。之所以冠以“长”字是与下面的“短流程”想对应。
短流程
是长流程中每个独立的方法或步骤。因为这些步骤或方法对事物的形态进行了改变或转换,所以也可以认为是一种流程,只是只有一步操作的流程而已。短流程具有长流程无法替代的优势,是本文所述方法的关键点之一。
技术官僚主义
由于应用对数据的封装性导致应用间数据共享困难从而影响业务工作顺利开展的现象。
协作平台
是一个系统,用于提供应用间的协作支持。这里特指应用本文理论构建的应用协作系统。
问题的引入
信息时代,程序员用灵巧的双手构建了一座座信息大厦,我们的工作、学习、生活越来越多的被涉及其中,并带给我们带来实实在在的便利。然而随着信息大厦不断加高和改造,一些原本不是很突出的问题现在变得严峻起来,甚至成为瓶颈。例如:如何对应用进行可视化管理复杂系统中如此众多的应用,如何更方便更容易的改造系统,如何保持系统间的数据一致性等等。下面我们就来深入分析一下这些问题的深层次原因。
系统的复杂性引入组织复杂性
计算机技术的产生必定会在某种程度上代替人的工作。系统的廉价性、高效性、准确性等诸多优点必然导致在职能分配中的优先地位,即使系统的研发成本很高,也无法动摇这一事实。我们不妨想象一下日均千万的订单如果没有一个信息化系统来支撑,我们需要多少人员,需要多少场地,需要多少……。这种优势地位会迫使企业在不断加大系统研发的投入力度,复杂系统正在成为新的“老板”,整个研发体系都在为这一新老板“打工”,由于复杂系统规模庞大、业务繁杂、变更频繁,这使得复杂系统中的特定应用群与组织中的特定部门有了映射关系。
系统结构影响组织结构的布局
为什么要这么映射呢?我想其中一个最好的理由就是并行开发。为什么BAT能够那么快的“借鉴”别人的创意,我想于此功不可没。他们有足够的资源进行并行开发从而使工期大为缩短。
另一个原因是,从应用变更的频率来看应用内部要远远高于接口的变化。为了减少变更的代价,就有必要使熟悉场景的人应对这些变化,于是一个相对稳定的团队就会被限定为特定的应用服务。所以不管有意无意,组织的划分和应用的划分联系在了一起,当系统复杂到一定程度后。系统的职能结构和组织结构基本上能保持一致。系统的规模越大这种现象越明显。
组织结构影响系统的效率
组织会随着系统的复杂化而复杂化。而复杂化的组织必然导致过多的部门协作成本。也就是说复杂系统,除了自身的复杂性外,还引入了组织复杂性。组织复杂性会在某种程度上会减弱并行开发所带来的效益,而且随着业务精细化运作的不断深化,业务规模不断的扩大,情况会越严重。
系统的结构变化与保持组织的相对稳定性是相互矛盾的。一般情况下组织的变化会滞后于系统的变化,这会引起一些问题。如当遇到系统职能变更与部门不是很匹配时,我们有时候考虑的不是系统的最优化,而是“当下”执行代价最小为原则。因为部门变更的代价往往会远高于系统合理化的代价。也就是说部门的结构有可能对系统的结构产生影响,而这种影响是非常负面的。等问题积累到一定程度后,我们不得不正面系统本身的问题。
有这么一个例子可以说明此问题——复用件。一开始是没有相应的部门来造复用件的,谁用谁造,所以重复造轮子的事情会不断上演,而且还会持续上演。这会为系统引入不必要的具有某种重复性的复杂性,当我们无法忍受这种“重复”时,通用而统一的设计才会被提上日程,才会有一个合适的部门来承担。
系统复杂性变迁
面向对象可以使系统化繁为简,造就了好多功能强大的系统。因为没有职责划分我们就没有模块化,也就不可能构筑今天这么一个复杂而繁华的互联网世界。只是时代在变迁,应用开发的生态环境也在发生着显著的变化,这些变化使得我们更加容易得构建此类系统。
在应用的发展的早期阶段,各类基础组件技术上还不是很成熟的,通用性和易用性都不是很好甚至是没有。如:如何分布、如何缓存、如何多线程、如何部署、如何均衡等等。很多都需要研发自己去实现,甚至是重复实现,这需要耗费很多的人力和时间,业务系统的开发效率自然比较低。
而今得益于开源领域的飞速发展,这些基础技术取得长足的发展,易用、健壮而稳定,我们不在为此而劳神,设计文档也越来越少,我已经在好多项目中看不到什么设计文档了。程序员现在专注于业务系统本身的复杂度。这使得系统越来越像“业务”的影子。
技术瓶颈消除了,我们便能够更快、更好的实现应用,然而技术的进步似乎并没有减少我们的加班时间。这可能与资本竞争与扩张有某种关系。但系统本身的问题却是无法忽略的。系统的规模迅速扩大,我们的接口越来越多,彼此之间错综复杂。要想在这样一个系统上保持功能的快速迭代实非易事,接口在提供并行开发机能的同时,同时也构筑了一道难以逾越的壁垒并形成新的瓶颈点。
应用间的硬性绑定
系统以接力棒的形式将不同应用串接起来,来共同完成一个业务流程。这种方式是应用间的直接耦合,是应用对流程的硬性绑定。流程一旦开发完成,流程就寄居于业务代码中了再也出不来了,从此失去了人身自由。这种硬性的绑定极大的扼杀了流程的灵活性,流程变更将变得极其困难,具体体现如下:
l 接口变更会要求调用者和实现者都要进行改变。这个改变可能需要跨越一个或多个部门,这需要大量的调研与协调成本。大量的跨部门的产品、项目、研发会议吞噬着我们的工作效率。
l 接口的改变原则上不能影响现有业务的正常运转,我们必须做过度处理或者兼容处理保证生产能够持续进行。这些会额外消耗我们很多资源和时间成本。
l 因为引入了一些过度手段,这就增加了一些“非必要的”复杂度。这些非必要复杂度可能在多次迭代中依然保留,并随着迭代次数增加而不断增加。因为排除它们并不能改善运行结果,很多团队宁愿把时间放到更有价值的问题上。这对系统的可维护性造成非常不利的影响,推倒重来的事情时有发生。或者是走向另外一个极端——失控,有些老接口没人敢动,便是这个原因。
l 设计文档的更新跟不上代码的迭代速度。导致每次迭代基本上都会在代码中隐藏一些细节。时间久了,我们只有翻代码才能了解到这些细节。这就更加加重了我们以后修改接口的困难程度。
数据“私有化”
计算机之所以被设计出来就是为了进行数据处理的,而处理一定是有结果的,这个结果就是另外一种数据。运行在计算机上的所有应用都必须有数据的输入和输出,这是应用价值所在。也就是数据是应用的主宰,应用为数据服务。
然而在一个复杂系统中应用是通过接口进行协作的,数据只是接口的参数而已,也就是说接口是数据的直接控制者,应用则是数据的间接控制者。我们很容易推导出业务是数据的更远的间接控制者。这个逻辑能工作,但不是个好的逻辑。因为如果我说的“控制”没有错的话,这与应用为数据服务相矛盾。
如果数据服务于业务成立的话,那么业务可以强迫应用为数据服务。但无可避免数据在为业务服务的同时也会顺带为应用服务这一事实。所以数据是有机会被应用“奴役”的,而且这样的机会很多。于是,接口一方面在构建复杂系统上居功至伟,而另一方却“绑架”和“挟持”数据。这也是现有复杂系统痛并快乐的原因所在。
世间万物分久必合合久必分,这在数据定义及存储上也被验证。最开始的时候数据是集中定义和存储的,但现在应用太多了,数据集中管理严重桎梏系统的发展,所以就分开了,于是各个应用分管了数据的定义和存储。这远比绑架和挟持严重的多,这是彻底的占有,我称之为“私有化”。私有化的弊端是多方面的,让我们来看一下.
首先,业务数据定义的规范性和科学性不能得到有效保障!系统在设计之初,我们在全局上的数据考虑是非常有价值的,但当系统进入运营状态以后,设计便开始失去其约束力,而系统的运行现状将成为越来越强的约束力。变更业务数据将更多的受到系统层面的限制。不管是人为或客观的原因,出于“当前最小代价”的考虑,我们经常会对“应用现状”做出某种妥协。
其次,数据污染现象严重。例如接口间的妥协变更便是对接口数据的一种外部污染,它可能会模糊不同业务间的边界,为以后的迭代造成更多的困惑。还有一种污染来源于内部设计,我想很多人都使用过“扩展”字段,而“扩展”二字的意义肯定不是将来可能要代表的某一具体业务数据的真实意义,这是一种概念上的污染,这是关系型数据库难以克服的痛。除了上面两种污染外还有一种是技术数据对业务数据的污染,比如版本号。有的版本号是一种纯技术处理手段,没有任何的业务意义,然而我经常发现它们未加隔离的和业务数据混在一起。它们就像寄生虫一样,侵蚀着应用的健康。总之不管哪种污染,都像自然环境的污染一样后果严重。因为治理成本要远远大于产生污染的成本。
再次,数据的共享性和一致性将会受到很大程度的制约。在数据集中存储的年代我们可以使用任何数据,但数据“私有化”后,大多数情况下,外部只有通过接口才可以得到数据。有时候数据的提供是一次性的,外部要想再次使用,则需要自己进行“私有化”。这种技术因素导致的“官僚主义”是信息化道路上的严重绊脚石。它会造成很多数据冗余及资源浪费,不但如此,还有最重要的一点是,这种冗余可能不一致,而这种不一致可能会造成一些不必要的麻烦。
最后,数据私有化对数据仓库的建设也有非常不利的影响。1、数据部门要一对多了解每个数据的意义,想一想一个部门要面对数千的职能研发人员,这个一对多有多么的恐怖。2、需要及时跟进业务变更情况。因为业务线非常多、非常长,大量的变更会集中到数据部门。3、“清洗”被“污染”的数据。
现有的解决方案
面向接口编程是一种优秀的思想,时至今日它照样有不可磨灭的生命力。但系统堆积成为复杂系统后,“涌现”出了一些原先不是问题的问题。这里借用了一下《失控》一书中的“涌现”的思想,其核心意思是,在群体上出现的特征,并不存在于某个独立的个体上,且只有当个体数量达到一定规模后才会出现。
针对接口过多的问题,虽然已经有一些解决方案,如京东用jsf(之前叫saf)淘宝用dubbo来对接口进行集中管理,并且在一定程度上简化了研发、部署和运维的工作。但它并没有将数据从接口中解脱,对于业务变更还是面临同样的问题。
还有一些方法如局部流程配置化,即用配置来包容更多的变化并减少对既有代码的修改。jBpm、Activiti、 WWF,以及规则处理引擎drolls等都是此类解决方案的代表。但据我了解,它们仅限于单个应用发挥作用,很难在全局上实施。同时流程的设计和变更需要由研发人员用另一种“代码”来表达,这不是产品和业务人员能玩的转的。还有要实现7*24小时无中断流程升级也是很困难的。
在这里不得不提一下我们经常使用的另外一种技术MQ,它在某种程度上解决了应用间耦合问题。但仅仅解决应用耦合是不够的,它还需要解决数据私有化的问题。
突破口
还有一些方法我们可以尝试,比如更好的抽象,使得接口更加稳定和包容。本文便是基于这种思路来实现的。抽象只是一种策略,并不是一种具体可实施的方案。要想解决复杂系统的诸多问题,凭空发明一种机制或方法来同时解决上面所提到的所有问题是不科学的,也是不现实的,我们需要找到问题的本质。其实我之前有过交代,那就是数据私有化。解决的方法可能有很多,本文只是其中之一,即便如此,这个方案也没有经过实践的检验,实难想象其效果。权作分享一下,抛砖引玉而已。
打倒“技术官僚主义”,使业务架构和技术架构互不牵绊,互不打架。我们需要摆脱应用对数据的实际控制能力。从技术上来看我们应该将业务数据的定义从应用中拿出来,以摆脱应用的控制。而应用是必须要处理数据的,所以我们还是无法摆脱接口,但我们可以来规范这个接口,并要求接口的定义由外部进行统一定义而不是应用本身来决定。
接口有两样东西最重要,那就是入参和出参。先解决入参的个数问题,这个好办,将多个简单参数聚合成一个复杂参数就可以了,这个复杂参数可以用Json或Map来满足不同类型的需要。为了区分不同业务,我们可以在参数中增加统一的“业务类型”属性。每个应用实现一个这样的接口,就可以实现同一个接口用不同的实现来处理不同的业务数据了。
我们同时还需要一个系统来协调所有参与协作的应用,由它来对数据进行规范、验证、赋权访问等数据管理工作。这个系统可以依据数据的不同业务类型通过上面所说的统一的接口对不同的应用进行调度并收集及处理结果。我将这样的一个系统称之为“协作平台”。
业务复杂性与技术复杂性
“怎么可能?”很多人会有许多疑问,请不要放弃下面的阅读!我并不是在这里标新立异,夸夸奇谈。我两年前一开始有这个想法的时候也觉得这是一件不可思议的事情。然而我始终有一个信念,任何复杂的事情都有一个非常简单的原则在支撑着它,我非常迫切的想找到它。而且有一点我是非常确认的,那就是业务复杂性和技术复杂性没有必然的线性关系。
当下我们的应用系统规模可能很大,功能可能很强,我们不得不承认这一点,他们处理了大量的数据,虽然花费了大量的人力和时间来开发这些系统,不管怎么样这些系统正在有效的运转。很多人包括以前的我认为,对于一个复杂系统,天经地义应该是复杂的,这是普遍的共识。这种认识根深蒂固,让人很难怀疑它的错误性。然而这确实是错误的!为什么业务复杂性必然要导致技术复杂性呢?这是两个完全不同的世界!
业务复杂性是由业务本身来决定的,在技术层面上我们无法化解这种复杂性,但技术复杂性我们却可以在技术自身上做文章。我们回到协作平台上,它需要接管本该由各个应用自身来完成一些任务:对数据进行规范、验证、赋权访问等。我们的任务就是要对这些职责进行足够的抽象,以使它们可以复用,以简化技术复杂度。
但这种抽象绝对不是某种技术上的抽象,而是业务上抽象。我们需要将业务相关性变成业务无关性。这需要找到业务的通性,并规避业务的个别性,并需要把业务的复杂性从技术复杂性中剥离出来,这也间接说明系统的技术性正在被业务性取代。然而这并不是一件很直观的事情,我们有非常多的干扰因素,加上业务之间的不同是如此之大,我们如何下手?我的做法是改变对现有业务形态的认识。并站在更高的位置上来审视这些业务,这样才有可能发现一些庐山真面目。
但要从“宏观”上来观察这些业务,还是有一些困难的,首先大多数研发人员的时间和精力往往局限于某一具体的业务,因为我们讨论的系统非常的大,所以只有少数有幸的人可以关注这个层面的内容;其次,即使运气足够好,这些有幸的人大多只关注普适的“技术性”问题,要想对“业务形态”在认识上进行改变,是非常小的概率事件;最后,即使我们拥有了一个足够棒的业务形态认识。还要克服实现过程中的各种困难险阻,如果改变认识形态是一种机遇性事件,那么真正花力气的地方就是实现了,毕竟说起来容易做起来难。
非常幸运的是我就是那个小概率事件中的其中之一。正如上面所说,其实现过程我大约花了两年的业余时间来做这件事情。认识形态只是一个概念的认识,但这个概念是不具体的,需要一整套完整的理论来支撑它,但理论的完善和实现是交替进行的。理论是实现的依据,实现可以发现理论的不足,两者结伴而行才有了今天的结果,这至少让我感觉我的理论不是那么的空洞。
单据
回归正题,我们来解决业务相关性问题。纵观各种业务流程,不管是信息化的还是非信息化的。单据是流程中是最为重要的东西,流程中的每个环节都需要依据单据进行工作,并产生新的单据以驱动下一个环节。发票、存款单、结算单、出库单、提货单……,这些都是单据。我们并不关心开单据的人是谁,也不关心这个人做了那些具体工作,我们只会关心票据本身所表达的意义。
单据就是证据也是数据,是对工作或行为的结果或状态的一种描述和确认,而无需关注过程的具体细节。尽管单据的内容是业务相关性的,但单据的处理模式是业务无关性的,这正是我们要找的东西!就是由N多种单据按某种顺序组合在一起的一个有序数据集合。而我们要实现的“协作平台”就是要对这个有序数据集合的管理和生产进行支持,而管理本身及业务相关性处理则是由使用这个系统的人和参与协作的应用来承担的。
协作方式
我们开了个好头,但还需要细化。协作平台中的 “协作”不是人之间的协作而是应用之间的协作。毕竟“如何做”平台是不知道的,还得 依靠各个业务应用来协力完成。协作平台会将所有参与的应用和数据串接起来构成一个完整的业务处理流程。但如何串接是个问题,这里的发挥空间很大,也是协作平台的本质和核心,以及难点所在。我们先来看看这个串接起来的样子,如下所示,便于以后讨论,我们称之为模式1。
数据->应用->数据->应用……数据 (模式1)
模式1代表了协作平台和各个业务应用实际工作时的样子。但我们现在需要把焦点放到协作平台上。假设其代表的是某一个特定处理流程,如前所述,那么其中的数据部分则是这个流程中的关键所在,是不可替代的,如果被替代那就不是这个流程了。但应用则是可以被轻易替换掉的,举个例子手机充电器就是一个充电的“应用”,我们不需要非得用原厂的,只要是有质量保证的220V-5V(源数据到目标数据)的充电器,我们一般都可以换着用。也就是说应用的替代并不会对流程造成什么影响。
我们这里还得说一下MQ,虽然我们前面否定了它,但它的一些特性还是值得我们注意的。最重要的一点是,它统一了和应用的接口,本身就是一个协作平台,只是缺少对数据的管理而已。但还不止这些,如果仔细观察模式1,我们会发现模式1的开始和结束都是“数据”,而MQ的两端是应用。
这个“不同”不是对同一事物的不同形式的表达,而是有着本质的区别。MQ只是数据的暂存和中转,它仅仅是一个通道而已,是不能作为终点的。它连接的是多个应用,服务的是应用而不是数据。而我们的协作平台是要服务于数据的而不是应用。
数据流
对MQ的关注给了我一个启示,模式1的开头和结尾都是数据,即中间无论怎么折腾,当我们给定一个输入应用就会给出一个确定的输出。且由于流程中的应用是可以替换的,所以它的重要性在这里我们可以先忽略一下。所以我们可以简化一下模式1,于是得到了模式2:
数据->数据->……数据 (模式2)
模式2明确指出流程的核心构成是数据,同时“->”也暗示了应用存在的理由,即数据是应用的“目标”。数据流中的每个数据都可以看作是整个应用系统各个工作阶段的“里程碑”,至于数据与数据之间如何转换,由谁去转换则是另外一个低级层面的内容。也就是说数据流强调的是“目标”层面的内容。
模式2的好处是,我们无需关心目标实现的复杂度,以及如何实现目标,但我们始终可以正确的到达终点目标。这是非常有价值的。它将应用从流程中进行了剥离,这使得应用变更相对于原先复杂系统中的应用变更变得代价很小、很容易且没有任何的不良的并发症。最重要的一点是,它使复杂系统的复杂性瞬间解构,应用间的层级紧耦会被打破,使整个系统扁平化,应用成为彼此独立的简单结构。模式2从理论层面上打破接口的壁垒效应,为我们利用协作平台对业务数据进行统一的规划和管理提供了保障,同时解决了数据不一致和分享的问题。
不但如此,模式2为产品人员直接维护业务系统提供了可能性,使之成为业务系统的真正的主人,不需要再跟在研发的屁股后面央求本该是自己拥有的东西了。研发则变得更为简单,只需要在数据间挂接应用就好,使沟通成本大为减少。这种简化的设计也必将对研发组织的构成和工作方式带来深远影响。
空间和时间
基于这种业务形态认识的转变,我们有了对协作平台的构想。虽然模式2的简洁和抽象可以让我们更好的包容变化。但我们需要一个可以运转的系统。所以我们还有很多的工作要做。
第一个问题就是“目标”管理。在一个复杂系统里,会产出非常多的不同类别的数据,我们必须采用一种有效得管理方式进行管理。其次,解决目标之间转换的技术问题,如分流、合流、状态、重入等等。因为数据是一种静态的是一种空间结构问题,而转换是一种动态行为的是一种时间顺序问题。所以我将此章名为“空间和时间”,本想用“时空”一词,但因为顺序问题而没有采用。也就是说“顺序”问题在这里很重要!
请各位不要误会!我不是故意要用这么大的一个题目。我需要对理论的完整性进行保障!时空是一个完整的概念,不能多也不能少。这同时也是“协作平台”所表达出来的一种设计理念,是对模式1的一种直观映射。
空间
这里的“空间”指的是业务领域空间,当我们的“目标”太多的时候,最好的做法就是分类,使其具有可收敛的层级结构。这里借用域名空间管理的方法为每个目标指定一个域。其名称即包含了自己的职能意义,也包含了所在职能的层级结构。但任何事情都有两面性,域一旦投入运营后,对其重命名将变得很麻烦,因为域下面挂接的东西可能非常的多。所以域的设立要十分的仔细,其原则是不能在空间上产生交集。
授权
基于域模型的树形结构对授权操作是非常方便的。拥有较高层次的域名访问权限便会拥有其下较低层次的域名的访问权限。而我们可操作的权限列表也是非常的简单,仅仅为有限的几个如:读、写、管理等等。与之相对照的是我所就职过的某家公司ERP系统中的可授权条目可以用眼花缭乱来形容,而且各个业务系统开发各自的授权系统也不是新鲜事。为什么会重复造轮子呢?我不想去追究,我只是想说协作平台已经抹去了不同业务系统的区别,他们只是数据访问而已。
基于这个授权模型,我们可以大幅度简化业务的管理问题,因为我们只对“域”这个主体对象进行授权管理就好,授权主体中不再是某一职能,不再是“应用”,这将大为简化授权主体的数量。不仅如此,域的层级结构使得继承的授权机制进一步压缩授权主体的数量。从而大为简化授权管理。
数据定义的版本
每个域都会唯一的绑定一个数据定义,这样可以保证数据定义被唯一标识,但我们要保证数据定义是可以改变的,但这种变化有个前提条件。就是数据定义的变化不能影响已有的业务运行,具体的讲就是不能影响到现有应用的工作。其解决方法也比较简单就是为数据定义增加版本号。不过现在如果需要唯一标识数据定义则需要用:域+版本号来共同标记。有了版本号后,对流程的支持将大为加强,如实现流程的兼容处理及灰度升级等。
集中存储
我必须要强调一下集中存储,如果存储由各个业务系统来承担,那么数据无法避免私有化问题。所以协作平台必须妥善来解决这个问题。我们要面对的是每天千万级乃至亿级的数据量,所以关系型数据库就不用考虑了。我们没得选,只有分布式的Key-Value数据库才能轻松的容纳我们的数据。我们现在还得验证一下我们的数据结构是否适用于Key-Value数据库。这一次我们将再一次惊叹于“域名空间”给我们的便利。
每个域名不就是Key-Value中的Key吗?不是的,域名只是数据定义的名字,我们还需要加上数据的版本号与数据实例的ID。所以Key的组成为:域名+版本号+实例ID。不过还有两个细节需要处理,这两个细节都是为了减少Key的长度而考虑的。第一个是域名尽可能的短;第二个是实例ID可以基于域名独立编号,也就是说两个不同域名的实例ID可能会相同,但因为有域名做前缀进行区分,所以不会混淆。Value就很简单了,直接是一个Json串搞定。
到这里“空间”部分似乎可以收官了,但我们遗忘了一个关键问题,那就是“关系”缺失的问题。在这里我要反问一下,在这种海量数据情况下,如果是基于分库分表我们还用Join查询吗?关系数据库中的“关系”已经名存实亡了,它所剩下了也只有按主键(Key)查询了,这不就和Key-Value的作用一样了么。
基于域管理的数据定义
时间
这里的“时间”不是指时间的本意,而是时间的特性:
首先,时间是一切变化的驱动者,没有时间就没有“动”,也就没有“流”。但有时间没有“空间”照样也形成不了动,所以我们的业务流(时间)要附属于数据(空间),而不是数据附属于流程。即这里强调一个重要原则:先有空间后有时间。想想很简单,先要有物才能谈得上物的运动,往往是这些基本和简单的原则导致物质形态认识的巨大不同!但如果我们事事搭乘自然规律的顺风车,我们必将会事半功倍。
其次,时间一旦过去就是历史。历史是不可逆的,这就意味着流程不可重来,如果做错了我们可以用其他流程,或者用一个新的数据实例来开始,所以本文里面是不会出现“循环”的流程。历史更不可以变更,但历史必须要有事实依据,形成事实(数据实例)的将在历史上记录一笔,但未形成事实的,我们可以再来!比如处理过程中的通讯异常,我们通常所说的“可重入”便是此种情况。
水流
相对于空间来讲,时间要复杂的多,要想用“时间”来描述流的特性我感觉还是比较吃力的。我没有办法用这个概念来讲解下面的内容。只好用另外一种具有“流”性质的自然事物进行讲解——水流。水流是符合上面讲的时间的两种特性的。首先水流是一种物质流,其构成为水分子,满足先有空间后有时间的特性。其次任何人不会趟过同一条河,满足时间的历史特性。除了这些特性外,下面我们还会讲到很多。
引水
当我们需要水的时候我们会在河道上开个口进行引水,而河道里的水是不能自由分配水该走哪个口的,这个完全由下游自己来决定。数据流也遵从了这一原则:即下游可以选择上游和上游无权分配下游。如果其他人想用,来“引水”就可以了,但给不给可以由上游来定。
这样每个流程都来自上游数据(或外部输入),因为不用决定下游,流程到自己就结束了,所以每个流程都非常的短,只包含上游(或外部)数据和自身数据两个节点,我把这个称之为短流程。短流程是流程中的最小可复用单位。这会极大增强流程拼装的灵活性,同时为流程优化提供更多的线索。
整个业务系统的流程都是由这种短流程组成,基本上可以等价的认为整个业务系统就只有一个流程。短流程真实还原了业务间的内在关系,它们是有机不可分割的,且没有任何多余的累赘。这和流程引擎中描述的长流程在作用上有着重大区别。
l 短流程为全网实施流程编排提供了可能,长流程必须以“整体”形式进行操作,在复杂系统中光是流程实例的上下文传递都会是一个问题!
l 可提供最大限度的流程优化线索,因为我可以同时看到所有的关系,清晰而完整。而彼此“分散”的长流程会将“多余”的关系隐藏,从而割裂关系网,使得我们不容易从全局看清问题。
l 短流程复用程度会很高,相同的片段不会出现两次。
l 改变流程时代价最小,我们每次只改一个或几个短流程而无需修改整个所谓的“长流程”就可以完成改变,且不会对现有生产流程造成任何影响,方便快速迭代。
l 短流程不需要上下文来实现状态跟踪,只要知道上下游的对应关系就可以了。这样每个短流程的都可以轻松的实现独立部署和负载均衡。
l 短流程没有条件判断,只要有“引水”者便可以往下流动。大大简化系统的设计,使系统可以高效的运转。
分流
如同江河的支流,有多少条河道就有多少个分流,没有什么其他复杂的逻辑。当同一个数据被其它多个数据选中作为自己的上游时,这样的情况就发生了。举一个例子。库房生产需要订单信息,写应收也需要订单信息。于是分流便自动形成了。
虽然业务流程是简单了,但在技术实现上还是有一定的复杂度的。主要的问题是同一个数据实例能否可以保证传递到所有的下游。这是个一对多的问题,我们没有关系数据库中的事务可用,但还是有方法的。先建一计划,其内容为上游数据实例和多个只有ID而无内容的下游数据实例的集合。只要计划保存成功就可以成为“事实”,然后异步分别填充这些空实例就好了。
这里有两点要说一下,第一、数据流都是异步的,我们无需等待它“成为事实”。如果要等系统可能会很惨,因为流程可能会很长。所以我们不要等,水流如果停下来那就不叫流了,除非到了终点。第二、这里没有事务,但会最终一致,计划只是保障数据不会突然消失或者突然冒出来,计划里的东西早晚要流下去的。
分流实例图
合流
有分就有合。还是拿订单举例,大家都应该非常熟悉订单状态跟踪吧,一般情况下,实现起来还是比较麻烦的。不过现在我们可以很简单的就能解决这个问题了,而且对其他业务模块是无侵入的。
在继续之前,我们需要解决一下状态的问题。我们在数据定义中附加了一个统一的状态字段。它是一个字符串数组,用来定义数据可能用到的所有状态。现在我们来定义一个订单状态,其内容包括“等待支付”,“已经付款”,“正在出库”等等。
接下来我们需要建立下面的数据流定义:
应收数据->订单状态数据
对账数据->订单状态数据
出库单数据->订单状态数据
……
对于同一笔订单来说,订单的状态数据应该只有一份与之对应,但应收、对账、出库都会修改订单的状态。这不是和时间的历史特性矛盾吗?我们必须坚持事实是不可变更的这一原则,否则我们的数据就会存在一致性问题!
有很多的方法可以处理这个问题,为状态数据增加历史列表就是其中之一,这个列表用来保存状态数据所发生的每一次变更。列表项的增加是有限制的,如果是同源(同一数据实例)则认为是“重入”,不再进行处理。
下面三张图是订单状态发生三次变化后的截图,前两次都是以历史数据的方式进行呈现。
订单状态:订单创建
订单状态:订单出库
订单状态:配送
状态数据与多生一
状态数据的引入可以进一步扩张我们的应用场景,比如用户多次支付的对账场景。在这个场景中只有一个流的定义:
实收数据->对账数据
如果对账数据不引入状态的话,我们是无法实现多次支付功能的。但这不是个“合流”的场景,因为它的源的类型是相同的只是实例不同,所以对账数据可以被多次被改变并生成历史记录,我们将这种场景定义为“多生一”。
对账后我们要依据不同的对账状态进行不同的后续操作。根据支付情况我们可能会有“未足额”、“足额”、“超额”中的某一种或两种状态。于是我们可以有下面的假定数据流设计:
对账+足额->出库单
对账+超额->退款单
请注意上面的“+”及后面的状态,这并不是一种状态触发或条件判断!,我们还是遵从“引流”的原则,这里只是对数据和状态同时做了选择。另外一点需要注意的是,退款单的退款金额应该如何得到以及如何保证不会多退?这个工作不是由协作平台而是由应用来承担的,做法是这样的,对账的每个历史版本中都会记录超出的额度。这个额度的结构等于 “当次”支付金额减去“上次”对账欠款,注意超额是不能够进行超额的累加!这样每个退款申请单就可以唯一对应一个对账的历史版本,从而保证数据的一致性处理。
对账与退款
一生多
我们上一小节讲到了多生一,那么有没有一生多呢?有!订单拆分就是一个例子,用户的订单中可能包含许多商品,这些商品可能分布在不同的库房中,这时候我们就需要按库房将用户订单进行拆分。业务流的定义如下:
用户订单数据->生产单数据
我们的系统是要一单一单进行处理的,我们如何保障所有的生产单都被提交并且一定会往下流转?方案和“分流”是一样的——计划,这里不再累述。
客户单和生产单域模型及流定义
一个客户单生成两个生产单
数据流定义与数据流实例的关系
分流与合流是由数据流的定义决定的,其生成的数据实例是分别对应“一对多”和“多对一”。而一生多和多生一则是由流程中的应用自己来决定的。对于一生多其源数据实例中一般包含着一个用于生成多个目标数据实例的列表;对于多生一则情况多少有些复杂,首先目标数据定义必须要有明确的状态定义,否则协作平台会生成多个之间毫无内在关系的目标数据实例,其次还需要应用对不同的源数据实例进行区别对待。
到目前为止我们还有一种最普通的关系没有讨论,那就是一生一和无分流的情况,这是最基本的处理,没有什么需要多说的。
四维时空
上一章节我们对数据和流在概念上进行了较为详细的描述,我们现在在架构层面再来审视一下我们的论题。请看下图。
企业的运行可以分为决策和执行两个层面,虽然我不懂企业管理,但凡事先想(决策)后做(执行)是一种靠谱的行为方式。所以我们也将本文的理论模型划分为决策和执行上下两个层。但每层又有空间和时间的划分。于是我们可以得到一张4维时空图。
横向来看,决策和执行被严格区分,同时决策意志将被严格执行。具体来讲,数据实例的产生必须依据数据定义,数据转换的运转必须依赖于某一特定的流程定义。纵向来看,左边为数据相关是空间属性,右边为转换相关是时间属性。数据是转换的源和目标,而转换是达成目标(数据)的手段或方法。
决策层
决策包含数据和流程的定义,而决策本身是一种空间定位所以归于空间维度,这种结构非常的轻,并且不再受固有应用的约束和限制,这大为增加决策的自由表现能力,使得我们可以更快、更规范的进行决策布局。决策者会真正成为系统的 “主人”,应用会“不打折扣”的遵从主人的意志,这种“意志”简洁而明确,不会被应用所污染和“监管”,这就大大提升了管理的有效性。
数据定义和流程定义虽然同时位于决策“空间”,但两者又是矛盾统一的。我们不用怀疑数据定义的“空间”属性,需要指出的是流程定义整体而言也是空间属性的,首先它是一种“定义”,这决定了它的空间属性,但流程定义又含有“流”这种动态的时间属性进行修饰,所以流程定义是空间和时间的复合体。
流程定义必须依托于数据定义,但其时间合理性(步骤是否合理)则可能反过来制约数据定义。这种对立统一直接和如实反应了客观世界的对立统一,这为我们的决策调整大开方便之门。一方面可以使我们对流程或者数据的调整更具有针对性和直观性,另一方面可以使数据定义和流程定义达到最优化的平衡,而这种平衡与其说是矛盾,不如说是种珠联璧合。
现在我们再来看一下整个决策层,用域的树形结构来纵向组织数据定义,用点对点的短链接形式来横向组织数据之间的流。这样就形成了纵横交错的网。也就是说,我们用这种简单的模型可以构件及其复杂的业务逻辑,而不失管理的简单性和直观性。不管业务有多深,流有多长,不管是全局还是局部,我们都可以轻易探查和管理。流程中不可能出现可以隐匿的数据或者是“私有”数据,任何数据我们可以明察秋毫。
执行层
我们再来看执行层面,执行是按照时间序列而行进的,所以归于时间维度。在此之上我们又将之细分为数据实例和数据转换两个部分。这里我们也不用质疑数据转换的时间属性,倒是需要关注一下“数据实例”。数据“实例”只有在特定时间下产生,所以整体从属于时间属性;实例为“物”所以从属于空间,所以数据实例也有一些空间属性。这样在执行层面也有一套完整而独立的时间和空间体系,即执行有自己的时空。
执行虽然有自己独立的时空,但却受上层决策空间的绝对限制。首先,数据实例必须屈从数据定义;其次,数据转换必须遵从流程定义将一个数据实例转换为另一个数据实例。这种绝对限制使得决策意志得以绝对有效传达!
意义
我们已经完成理论层面的解释,下面我们将重点探讨一下其潜在的应用价值。
业务决策布局信息化
基于域模型来管理的数据定义集合实际上是一种业务战略布局,是一种业务决策数据的信息化表达。这和传统的方式有着重要的不同。传统的决策数据还是非常原始的,或非信息化的或部分信息化的或只是一座座彼此孤立的信息孤岛;它们大多只是概念化的、缺少细节的。这就决定了传统决策信息的模糊性、不易共享性等诸多不利因素将直接或间接导致传达的效果不是很理想,并伴生过多的沟通、传播、校正、学习及实施成本并带来不同程度的业务建设上的滞涨。
这要归罪于应用对数据的倒置管理问题!使得数据不能够由上而下进行规范和定义。当采用协作平台后,数据成为应用的主人,原先封装在应用中的“知识”现在全部可以转移到决策层。这些问题将不复存在。主要体现在下面几个方面。
l 决策数据会被集中管理。我们完全可用一张图对所有的决策数据进行展示和操作,让我们可以轻松掌控全局。所有的人包括决策者,实施者看到的都是相同的数据,使决策布局变得清晰明了。
l 统一和规范的决策数据描述。为实施方式的统一提供的前提。
l 数据的业务意义非常的纯粹和清晰。其业务意义被一致表达,为快速、准确实施提供了保障。
全局即时流程编排
我们再来看一下决策数据中的流程定义,它将整个业务体系串接起来,使得决策者非常容易的操控整个业务的来龙去脉。而规则引擎、jBPM等大都局限于一个应用中,流程虽然与代码进行了解耦,但应用之间流程是彼此分散的、缺少联系的,这阻碍全局观的形成,不利于流程改造。而流程定义的“短流程”形式可以连接几乎所有的业务点,轻而易举可以实现“全局”。全局为运营者带来了全局观,使我们的业务流程更容易优化。
要对流程进行即时编辑,首要的条件是流程可见。现有的大部系统流程是隐身于代码之中是不可见的。jBPM及规则引擎等虽然提供了一定的流程可见性,但它们却引入了另外一种只有专业技术人员才能驾驭的“编程语言”!而协作平台能够在决策层提供流程定义的完全信息化,从而提供一种即时的、可见的、可理解的操作体验。其次流程的编辑不能影响到正在运行的业务系统,这要归功于流程定义(模式2)的组成。由于全部由数据组成,和应用没有任何直接的关系,所以“可以”不影响现有系统的运行的前提下,进行流程改造和业务扩展。
简单而高效的研发
协作平台的出现将直接对应用的职能进行相当可观的简化。这些简化必然会对程序的健壮性、可用性、可维护性带来积极的影响,并可大幅度提高研发效率。下面我们就来详细看一下。
l 由于去除了业务间关系等复杂度,减少了协作和沟通成本,减少了开发和测试的复杂度。应用变更也会是一件相当轻松的事,就像“热插拔”一个硬盘一样。同时应用间的扁平化,为打破以职能为单位的组织结构,缩小研发规模提供了技术保障。
l 外部通信变得简单,只需和协作平台进行统一的交互。
l 去除了数据持久化的需要。数据的持久化会被协作平台集中处理,研发不需要关心数据表的结构,不需要关心用mysql还是其他数据库,不需要关心SQL优化,不需要关心是不是需要分库分表。
l 我们不用关心数据的一致性、重入、网络异常等的相关的问题。这些都会由协作平台来承担。
l 应用性能扩展容易,只需要增加节点的部署就好。
l 与线上环境高度一致的测试环境。
l 监控埋点的事情可以少做了,协作平台会在每个几点进行监控。
就个人经验来看,基于业务系统的应用开发最耗费时间和精力的莫过于数据的规范、存储以及数据在不同应用间的传递。有了协作平台后应用开发再无需关注这些内容,这些已经由业务决策人员和协作平台来承担了。剩下的也是应用最应该做的——数据转换。
数据分析
决策数据的规划化,信息化具有深远的影响,它对整个复杂系统的生态环境带来全面的影响。其中也包括数据分析领域。首先数据获取将变得简单,主要体现在几个方面。
l 大幅度减少对接成本。对接的外部数据源由原来的一对多变为一对一,数据可以由协作平台统一提供给数据分析平台。
l 为数据分析和挖掘提供优质的数据源。数据是规范统一的没有被应用污染的,,减少了对数据的清洗工作。
l 数据是一致的,没有冗余的。不用担心不同应用对同一事物的不同表达所造成的不一致问题。
简化、优化、强化运维和运营
由于协作平台将复杂系统化解成了具有扁平结构的以协作平台为中心的系统,这会促使应用群的规模变小数量变少,这对运维的简化是很直观的。同时应用职能的简化、统一的存储、重复数据的减少。这些都会促使机器需要的减少,配置项目数量的减少。运维将变得比以前轻松很多。
我们的重点是运营,系统的运营的工作量要远远大于建造一个系统的工作量,对于一个复杂系统,其应用的管理成本是非常高昂的。应用之间的协作越复杂这种成本就越高,应为我们越来越不容易理清应用间的关系。我们知道业务和应用的虽然有一定的映射关系,但不是严谨和规范的是非信息化驱动的。这使得业务扩展、变更成本很昂贵,其最直接的表现就是,我们还没有一种好的方式 “即时可视化”应用之间或应用与业务的关系。
前文已经讲过决策数据的可视化,现在具体讲一下,它包含两个全局图。一个是业务布局全局图,是用域的树形结构进行组织的,便于层级管理;另外一个是业务流程全局图,此图可以显示每个短流程的实时流量统计,可以为新增业务或业务变更及故障分析等提供帮助,也是一个非常好的监控界面。
业务布局全局图
业务流程全局图
除了上面的两种图外,还有另外一个图对运营特别重要,那就是业务实例跟踪图。得益于协作平台对流程的统一管理,我们可以记录任何一对流经短流程的两个数据实例。并将它们如同模式2那样串接起来。如下:
数据实例<->数据实例<->…数据实例
请注意上面的箭头是双向的,也就是说我们可以在任意一点轻松检索整笔业务的来龙去脉。此关联全面、直观、而且没有任何遗漏!这极大的方便问题的跟踪和处理。这种可视化为我们开启了一种全新的运营体验,进入各个业务子系统逐步排查问题的年代可以终结了。
业务实例跟踪图
总结
到这里到了本文该结束的时候了,我们简单的总结一下。首先,我们指出复杂系统之所以复杂是因为其中包含了过多的业务定制性的内容,这种定制的内容与代码进行了紧耦合。其次,我们对所有的业务处理进行了高度抽象,提炼出了模式2。接着,我们以模式2为基础利用时空概念进行丰富,尤其是对时间和水流概念的引入,使我们能够构建一个使复杂系统扁平化的协作平台。最后指出协作平台所带来的决策信息化将会对降低研发成本及提高运营效率及质量起到积极的推动作用。总之,我最终的目的是让合适的人做合适的事,业务上的事还是让业务来吧。
最后祈求各位读者的包容与宽恕,因为我想写这么多内容文字不出错的概率太小了,另外受视野的局限性有些东西我可能描述的不是很准确,还请大家批评指正。
感谢:
郎帅是我的同事,感谢他“牺牲”了一些饭后散步的谈资,和我共同讨论了大量了有关协作平台的方方面面的事。同时感谢那些给了我意见和支持的人。
以上是关于李学斌:论应用间协作V3的主要内容,如果未能解决你的问题,请参考以下文章
活字格企业Web应用生成器V3.0发布更新,支持插件管理和多人协作开发
音频单元 (v3) 可以替换应用间音频以将音频发送到主机应用程序吗?