背景
做一个项目的时候,遇到一个问题。如图所示,流程如下:
STEP1: 协议发起协议下单,下单成功后,获取 E 单号;
STEP2: 协议使用 E 单号调用代扣款服务;
STEP3: 代扣款服务将 E 单号转为 X 单号,然后使用支付收单服务,支付成功。
STEP4: 交易监听收单消息,进行支付回调处理。
问题来了:由于代扣款服务将 E 单号转为 X 单号,支付给交易的消息中,只有 X 单号。交易不识别 X 单号,报订单号非法。
讨论
直觉上会认为:交易应该拿到 X 单号对应的 E 单号,然后去进行后续处理。 要完成这个功能,确实可以这么做。但是,这样做合适吗?
角色的相对性与转换
在这个示例中,容易意识到:这里需要有一个流程的全局主导者,将协议下单、支付、完成的生命周期串联起来。
从业务上来说,这是个协议下单的业务,理论上,协议应该就是业务方; 从流程上看,协议方下单、调用扣款,做的也是控制流程的事情。 不过,协议方认为,自己只是协议服务,不应该关注这么多东西。
不过我觉得,角色定义是相对的,是可以在基础服务和全局流控之间转换的。比如交易,在下单流程中,交易就是主导者角色,调用商品、营销、支付、会员等,将交易相关的信息聚合起来落库,将整个购买流程串起来;但对于更上游的业务,比如微商城、零售、教育,交易又作为一个基础服务模块。
同理,协议方虽然也是一个基础服务,不应该关注支付、订单之类的事情,但在协议下单这个业务语境里,它应该担当一个流控主导者角色。
能做与应该做
在讨论问题时,常常会出现一些模糊边界,这个边界的每个服务模块都可以做,但是,—— 能做,就应该去做吗 ?
这里涉及到定位的问题。交易的定位是:订单的创建、订单的生命周期管理。协议也有自己的定位:处理会员与商家之间的关系。
看上去,对于这个示例,交易和协议的定位都不太适合去做这个事情。
通用能力
协议同学又提出了一个观点: 如果有业务方 A,B,C,也需要下单成功后代扣款能力,但不需要协议能力,是不是每个业务方都需要做相似的事情 ? 是否可以提炼为通用的能力 ?
好问题。 看上去,交易吃掉这块逻辑,无论对交易还是对业务方都有益。
仔细分析下。 通用能力,通常包含相同的部分和相异的部分。相同的部分可以复用,相异的部分则使用扩展点来实现。
在这个示例里:相同的部分显而易见:下单成功 -> 代扣款服务 -> 监听收单消息,将订单状态改为已支付。 一气呵成,多好 !
那么,有什么潜在的问题呢 ?
如果代扣款失败,该怎么处理 ?
如果支付成功后,业务方需要做一些处理,再修改订单状态,怎么办 ?
有人说,这两个问题都可以通过扩展点来实现呀!这里又衍生出两个话题。
过度设计
考虑系统的通用性和可扩展性是合理的,不过,是否要在最初就做的很完善呢 ? 通过扩展点的方式,看似可以完美支持这些功能,但是,如果后续很长一段时间并不需要类似能力,这个扩展点的设计和实现就容易变成一个复杂度高、可维护性低、成本高的过度设计的方案。
此外,交易核心流程依赖上游扩展点实现,基础服务依赖业务实现,是依赖的反模式,容易导致可靠性问题。 可见,扩展点虽然万能,却不能滥用。
复用模式
理想化的复用模式是:定义一个万能的流程,这个流程中有很多插槽,这些插槽都是扩展点,可以供上游实现和插入。
实际系统中,对于比较常用的核心的地方,会定义一些插槽。可是:是否插槽越多越细,支持的能力越灵活越好呢 ?
还有一种相对低级而有效的复用模式:就是提供一系列拼板,上游可以根据自己的需求去拼接自己的业务图景。
一种是在核心流程中插入许多细小的插槽,业务方可以插入自己的实现,来完成定制化的能力。但这种方式有个弊端: 插槽越多,就越难理解和维护;核心流程依赖业务方的实现越多,核心流程就可能越不稳定。
一种是基础服务方只提供基础拼板,由业务方自行根据需求去拼接。这种方式,业务方更灵活自由,但是也会有很多相似的东西要重复做。
你更倾向于哪一种呢 ? 是否有更好的方案来解决这个问题 ?
先例
遇到问题,寻找可供参考的先例也是一种办法。
比如零售,也遇到过类似问题。当时采取的办法就是: 零售去做一层收单号与订单号的转换,然后调用交易提供的支付回调服务,将订单状态更新为已支付。
先例是否能够用到当前场景下,也值得仔细思考。
成本衡量
有一个重要因素:实现成本和风险考量。
交易:需要调用代扣款服务的查询接口,做一层转换处理。 风险:由于交易订单量非常大,而真正需要调用代扣款服务接口的订单量很小,很可能导致:a. 95% 以上的订单都没必要调用这个接口; b. 代扣款服务容易被拉挂; c. 代扣款服务如果被拉挂,交易的核心流程的稳定性会受到严重影响。
协议:需要自己去调用代扣款服务查询接口,做一层转换,然后调用交易提供的支付回调服务,将订单状态更新为已支付。 风险:无。不过不可复用,而且看似不太适合协议的定位。
代扣款服务:做一层转换,将 X 单号转成 E 单号,然后透传给收单服务,收单服务发送跟之前一样的支付收单消息。风险 : 可能影响支付的一些原有的服务。
从实现成本和风险角度来看,协议更适合做这件事。
小结
在中大型项目中,常常会有一些由于模糊的系统边界而导致的“灰色功能区域”。每个毗邻“灰色区域”的系统,似乎都有权利去实现这个灰色功能区域。那么,谁应该做这件事呢 ? 这篇文章提出了一些基本的考量因素:
- 在当前业务场景下,谁是流程的主控方 ?
- 在当前业务场景下,谁拥有信息的解释权 ?
- 系统的定位是怎样的 ?是否适合承接“灰色区域”的功能需求 ?
- 在考虑通用能力的同时,是否有过度设计的嫌疑 ?
- 实现方式是否容易理解、维护、可靠、稳定 ?
- 采用何种复用的形式更适宜 ?
- 实现成本和风险如何 ?
- 是否有先例可以参考 ?