当转转严选订单遇到状态机
状态机简介
这里所说的状态机,全名为确定性有穷状态自动机,也常被简称为有穷自动机,简写 FSM。在软件领域中,被广泛应用,如编译,正则表达式识别,游戏开发。状态机维护一组状态集合,和事件集合,能够对特定的事件输入,作出状态流转,并执行相应的动作。
状态机要素
状态集合(states)
事件集合(events)
检测器(guards)
转换器(transitions)
上下文(context)
业务系统使用范围
在互联网业务系统中,所有涉及到包含复杂状态的单据的业务场景,都可以使用状态机。
业务系统应用
在业务系统中,通过对状态跳转图的配置,以及对状态跳转的业务逻辑封装,完成系统对某一个特定的事件输入(接口请求),做出状态的跳转和对应业务逻辑的执行。
状态机的优势
首先说什么样的代码是好代码,最直观的感受就是,一看就懂,就是所谓的逻辑清晰,换句话说,就是代码表达的思路,符合大多数人对于问题的思考方式。人天生就对复杂的东西感到厌恶,喜欢简单的东西,这就决定了,人很难直接解决复杂的问题。而复杂的问题,往往可以看成是很多简单问题的组合。在漫长的实践中,我们学习基础,慢慢的掌握了对简单问题的解决方案,然而对于复杂问题,我们还需要掌握如何把复杂问题拆分成一个一个简单问题,这也就是分的思想。对于代码的架构,我们依然是使用这个思想来思考的。从业务逻辑开始变得复杂那一刻起,人们就不断在思考如何将问题变得简单,用分的思想,出现了分层架构,将业务逻辑,控制逻辑与数据分离。当业务逻辑进一步复杂时,我们用 DDD 的思想,将复杂的业务逻辑,拆分到一个个领域对象的行为当中,再通过领域服务用易理解的方式将它们组织在一起。
在业务系统中,应用状态机,不仅仅意味着,使用了状态机的组件,还意味着代码架构的选型,可以称之为“状态机架构”。因为它能够很好的拆分了我们的代码逻辑,并通过特定的机制将不同的模块连接在一起,完成系统的功能。这也就是状态机在系统中起到的指导设计的作用。它能让我们的系统,在长久的迭代中,保持基本的逻辑拆分原则。
除了拆分业务逻辑外,它还把控制逻辑和业务逻辑进行了分离。我们上学时都学过这样一个等式,程序=数据结构+算法,后来一位名为 Robert Kowalski 的大师进一步论证了,算法=逻辑+控制,并提出如果逻辑和控制分离的话,将会让代码更好维护。在业务逻辑复杂的今天,我们本就应该专注于编写业务逻辑,控制逻辑就应该交尽量交给框架去解决。状态机的引入,也就意味着,它帮我们分担了这部分工作。
最后,直观上看,我们的代码基本消除了对状态判断的 if...else...代码,这些代码穿插在业务逻辑中,就好比你坐在桌前思考问题时,有一只苍蝇在你眼前晃来晃去。
综述,状态机的引入,能够使代码的学习成本降低,也能够使维护成本降低。
状态机在转转严选交易的实践
理解业务
首先,需要绘制状态流转图。要整体理解业务,把单据按业务规则,抽象出一个一个状态,并且明确,什么动作可以促使它从一个状态变成另一个状态。比如,转转严选的订单,用户下单了之后,系统会生成一个订单,此时应该是“待支付”,那么当用户支付后,系统收到了支付消息,也就是收到了“支付”这个事件,就应该从待支付跳转到“已支付”。在这个过程中,我们看到的“状态”,椭圆一个一个画出来,我们提到的“事件”,用文字写在在两个可以流转的“状态”椭圆之间,然后把两个状态按照流转方向用箭头连接。把所有的状态和事件都画好后,状态机的状态跳转图就呈现出来了。
配置状态机
然后,根据把状态流转图,转换为我们的代码,为了便于理解,我们可以把代码设计的更加贴近自然语言。比如从“待支付”到“已支付”的代码可以是这样写的。
在这一个配置组合的编码之前,我们还需要把每一个状态,每一个事件都放到枚举里定义好。
把所有的状态和事件写好后,我们的配置代码是这样的:
业务逻辑
状态机的 transition(转换器)是用来执行状态跳转时需要做的事情。所以,需要把我们的业务逻辑,写进对应的 transition 中,如果不需要执行动作,可以定义一个空的 transition
下一步,就是状态机的触发了,也就是输入事件。这一部分逻辑,可以放到分层架构的 service 层,当然也可以放到 facade 层,这取决于你如何设计的系统的架构。这里做事件触发时,需要传入一个上下文信息,来告知状态机当前的初态和事件,也可以传入一些自定义的内容,以便业务逻辑执行时使用。
异常情况
执行以上方法,状态机就会自动帮我们调用 BuyerPayTransition 中的逻辑。那么如果出现了异常情况会发生什么呢,比如当前订单已经退过款了,但是系统重复收到了一个退款事件,当然不能重复执行一次退款。首先为了防止并发问题,我们修改订单状态时,要使用类似于乐观锁的机制。
然后,状态机在选择逻辑时,发现初始状态为“已退款”,事件为“申请退款”,没有可以执行的逻辑分支,这个时候我们可以选择让状态机抛出异常,或者我们定义一个回调,来打印一些友好的信息,或做一些记录。
可扩展性考虑
如果有一天,转转在售卖严选手机订单的同时,用户只需支付 1 元钱即可加购一个手机壳,并且在手机退款时,手机壳必须要同时帮用户退款,如何做呢。按照上边的设计思路,应该这样写:
虽然这样写没什么问题,但是这把两个业务流程耦合在一起了,如果明天需要再加个数据线,后天再加个贴膜...代码就会慢慢腐化,逻辑臃肿,架构坍塌。为了解决这个问题,我们可以设计一个注解,来监听严选手机订单的状态机动作。
写在最后
状态机不是什么高级的技术,重点在于让你用另一种思路去理解,去设计系统,以达到我们想要的目的。生活亦是如此,换一种眼观去看待事物,去理解世界,我们能生活的更幸福。(全文完)
> 转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。
> 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~
版权声明: 本文为 InfoQ 作者【转转技术团队】的原创文章。
原文链接:【http://xie.infoq.cn/article/79c042686f2412f05e4119d8a】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论