在前两篇《不要脸》系列文章中,谈到了建筑师冯果川说:中国的公共建筑前面有一个超尺度的广场,是为了留出足够的距离让你欣赏他正面的脸面,这种要脸的建筑以大块的空间,阻碍了和人、和自然之间的顺畅交流,空间层次单一而呆板。
而冯果川他们要把大块的地块切碎,让人能够走进去,设计成不要脸的建筑,把整体的单一建筑分割成可以随意布置的多个小的建筑,不仅丰富了内外的层次和变化,而且也便于和人、和自然的交流。
前两篇文章,分别从业务构件和数据模型层面,把要脸的、庞大的构件和数据模型拆分成不要脸的、粒度合适的构件和领域模型,从而为把要脸的、庞大的应用拆分成不要脸的、粒度合适的微服务架构应用打下了良好的基础。
本篇文章,我们讲一讲如何实现复杂的业务过程。
我们知道,CRM的最终目的是要满足端到端的业务场景,即从客户发起最终再将结果反馈给客户的、端到端的闭环处理过程。
下图是TMForum的etom描述的6个以客户为中心的端到端的业务过程:
图中etom的6个以客户为中心的核心业务过程是:
1. 从请求到回答;
2. 从订单到支付;
3. 从使用到支付;
4. 从请求(变更)到(完成)变更;
5.从终止(请求)到(终止)确认;
6.从抱怨(投诉、障碍申告)到解决方案。
看上去上述的每一个过程都是从头到尾的一条直线,似乎很简单,但真正业务场景可没有这么简单,每个环节能不能执行,下一步应该执行什么,都有一系列的业务逻辑判断,不同的判断结果导致不同的分支。流程越长,分支就越多,最后复杂一点的业务流程就像一张蜘蛛网一样,而不再像一条直线了,如下图所示是一张比较正常的流程图:
这还不算太复杂,早年在某电信看到的一张宽带安装流程图,整整挂满了一面墙,包含了各种各样的异常情况的处理,比如端口不够怎么办、地址未覆盖怎么办、联系不到客户怎么办等等。
CRM系统的目的就是用技术手段让IT系统能够快速、准确地支撑这些以客户为中心的端到端的、复杂的业务过程。
当然为了使这些端到端的业务过程能够运行,还需要一系列的内部管理过程,比如客户管理、产品管理、资源管理等。
目前业务过程的主流实现方式,主要是通过工作流引擎。
1、要脸的工作流流程
无论采用什么工作流引擎,实现的方式都是通过图形化的绘制工具,将从开始到结束的整个业务流程中的节点一个一个串接起来,其中的每个节点称之为活动(Acitivity)。
活动有两类,一种是自动节点或者叫服务,另一种叫人工节点或人工任务。
流程中的节点都是直接调用服务执行,一直到人工节点,会等待人工回复任务后,才继续执行剩余的环节。
使用工作流引擎的好处如下:
1、过程显性化:图示化的、明确的业务过程,而不是深埋在代码中的逻辑,可以更容易理解和更改;
2、状态处理:如果有异步节点或者人工节点,工作流引擎会处理每个流程实例的持久化;
3、状态透明:通过询问工作流引擎可以轻松检查流程实例的状态。可以直接在图形中监视进行的进度。
4、业务可见:可以在业务方与IT方之间,开发者之间,开发者与操作者之间,用可见的图形模型来讨论业务流程。
虽然使用工作流实现业务有诸多好处,但也存在很严重的问题:
复杂的业务因为包含很多的业务逻辑,因此流程需要加上很多判断节点,由硬编码的业务规则和是/否决策节点决定应该在不同的场景下执行哪个不同的分支。
这种基于工作流程的方法要求所有潜在的异常都包含在流程模板中,基于复杂的现实世界需求,会形成庞大的、难以理解的图。
这种将所有业务逻辑和分支集合在一起的工作流模板,是一种非常紧耦合的实现方式,一旦任何环节有修改,都有可能因为一些无关紧要、非实时的功能出现的错误而影响核心业务的完成,比如订购流程中因为最后一步通知客户发生错误,而导致订单业务不能及时完成。
为了避免一张图过于耦合导致的业务关连性的问题,在实践过程中,我们常常按照客户类型、产品类型、渠道、动作等多个重要的纬度为参数,去设计出不同的、细分的业务场景,针对每个场景建立一套对应的工作流模板。
这样虽然可以进行一定程度的解耦,但也带来了新的问题,即相似的流程过多。有些开发人员明明可以通过修改已有流程来满足新需求,因为怕导致已上线业务出问题,宁可复制修改一条新流程,也不会冒险修改已有流程。这样导致相似的流程很多,业务需求变更时,相关的模板需要一一进行修改,不仅工作量大,而且容易因漏改而导致业务不一致。
那么有没有更好的实现方式呢?
2、事件驱动架构
日常生活中,你醒来时,根据温度情况来决定穿什么衣服、穿几件衣服;开车时,看到红灯时会停车、根据不同情形会避开行人;在回家路上收到老婆短信,会偏离既定的路线专门绕路去某家熟菜店带点熟菜回家,否则会面临跪榴莲壳的危险。
日常工作中,你会查收邮件,对邮件做出回复,并对突发事件作出紧急处理。
这些其实都是根据事件作出的响应。
所以应用架构还有一种事件驱动架构(EDA),即业务流程节点间的串接,不是通过直接的服务编排和调用,而是一方发出事件,其他相关方订阅并响应事件,从而完成业务过程。
事件驱动架构的好处:
1、允许业务人员根据业务发生时的特定环境而做出对应的响应;
2、可以增加、删除事件的订阅者而不影响复杂的业务流程;
3、就像GPS允许用户根据不同的情形选择不同的选项和线路一样,事件驱动不是强制性的。
基于工作流和基于事件的区别,最好的比喻是:你并非是在火车轨道上按照既定的路线机械运行,而是使用GPS导航、根据不同的路况可以进行调整的驾驶。
最近微服务和“领域驱动设计”得到越来越多的使用,这些方法论尊重有界的上下文,即:在负责它的组件中实现业务逻辑并保持每个组件尽可能独立。
通过事件将要脸的、庞大的业务流程,拆分为一个个相对独立的微服务,每个服务之间通过事件,而不是直接的服务调用,来避免系统实现的耦合。
3、避免事件链
在事件驱动架构中,不需要有任何控制的中央大脑,组件间的交互都使用事件。事件驱动的架构解耦的诱惑如此之大,以至于会产生以下冲动:全部基于事件构造整个业务系统。
这种机制是没有中央控制器的,每个构件都是平等的,之间的交互都通过发出领域事件、响应领域事件来进行。
如下面的订单交付流程:
上图中,付款微服务直接监听下单事件,意味着每当有新的场景需要付款时,付款服务都必须进行监听。
这就违背了微服务自治的原则。
举例来说,如果要上线“付费下载音乐”的新业务,就必须与支付团队协调,因为他们必须开始监听“付费音乐下载”的新事件,那付费微服务就和订购、下载这两个微服务耦合了。
又如,订购一个GSM套餐,普通客户需要先收取一次性费用,VIP客户只要生成账单下个月销账就可以了。付款服务需要判断订购用户的身份级别,这样付费服务就增加了除了付费之外的逻辑,这也造成了两个微服务之间的耦合。
另外,纯粹事件驱动的架构,虽然耦合度较低,设置简单,也有这样的问题,即对于一些流程性很强的业务,如订单,很难看到端到端的、可视化的流程,因为并没有预先定义这样的流程。通常情况下,唯一能够找出这种流程的方法,就是从运行历史中找出所有的事件轨迹并进行串联。但这可能使调试和修改这样的流程都变得困难。
4、事件和流程的混合架构
很多流程性很强的业务,其实是需要一个协调器的,用来协调整个业务流程中需要参与的多个业务构件。
协调的形式不是强耦合的服务调用,而是通过将需要执行的命令以事件的形式发布到事件引擎,由相关构件监听事件并执行相应的动作。
如下图中,订单构件负责协调从下单、支付、出库、物流的整个订购过程中的所有活动。
这种工作流和事件混合的应用架构,可以带来工作流和事件两种机制的好处,尤其是在微服务架构时,能够很好地解决以下问题:
1、超时处理:
如果某些消息没有及时到达,工作流引擎可以跟踪时间并自动执行其他操作或切换到流程中的另一个路径。
2、消息关联和协调:
有时候,属于同一流程实例的几条消息必须在流程实例中合并。在工作流引擎的支持下很容易做到,因为工作流引擎可以根据当前持久状态和传入消息来决定接下来发生什么。在BPMN中已经解决了消息序列、消息同步、超时等待消息和消息互斥等多种通信模式。
3、错误处理:
无论何时发生错误,尤其是在进行同步调用时,可以指定应对的行为,如走其他分支或重试。
4、补偿:
可以采用补偿机制来撤销已经执行的步骤。
下图是用BMPN表达的事件和流程混合方式实现的订单流程示例。
上图中,Payment构件的支付服务也是一段工作流引擎支撑的流程,这个流程引擎嵌入在支付微服务当中,是由订单协调器构件发起的请求支付事件触发的。
5、结束语
用工作流的架构,还是用事件驱动架构?在这两种看似完全对立的架构之间,我们采用把流程和事件结合起来的一种混合架构,使得这种应用架构同时具备了两种架构带来的好处。
可以看到,通过这种混合架构,我们把一个要脸的、蜘蛛网状的复杂业务流程,拆分为多个小的、稳定的子流程,再建立担任协调作用的主流程,主流程实现相关的业务规则和判断,并决定要不要请求其他构件、哪些构件来协同,主流程和需要协同的子流程之间通过事件连接起来,实现了支撑业务过程的具有可拆分、可重用、可组装、稳定、易扩展等优点的应用架构。
参考材料:
《Microservices and BPM》,camunda