BFF治理与优化实践
结合一个典型案例的BFF治理思考
原文地址:麻广广的博客
在Thoughtworks的第25期 技术雷达中有一个主题:便利背后的陷阱,再次强调了软件开发的一种反模式:
信息
开发团队一般为了图方便,而在其技术生态系统的复杂环节中,引入一些不合理的行为,从而欠下长期存在的技术债务,并会引发更糟的问题。这种例子不胜枚举,包括将数据库作为集成点,使用 Kafka 作为全局编排器,以及将业务逻辑代码混杂到基础设施代码中。 诚如雷达中所说,这种反模式在软件开发中不胜枚举,开发团队必须时刻警惕,精心设计,持续地进行软件架构治理活动,避免为图一时之便而选择不合理的设计,欠下更多难以偿还的 技术债。
为了更深刻理解这种反模式带来的问题,我们先来看一个典型案例:
从一个BFF优化案例谈起
这个案例选择了一个在BFF(Backends For Frontends)中实现稍复杂的接口,但并不是最复杂的接口。接口的逻辑参见下图:
可视化永远是发现问题最好的手段,上图中描述的接口复杂度一目了然,甚至有点触目惊心,一个API背后的逻辑居然能复杂到这个程度,如果不加梳理并可视化出来我们可能没有这么直观的感觉。这是在BFF暴露给前端的接口,实现过程中需要调用后端10+个服务,进行几十次的服务间调用。在这几十次服务间调用过程中,有大量的读操作,以及近10次的跨服务写操作,读操作主要为了满足写操作的数据组装要求。
为了更清楚地理解这个接口设计背后的原因,我们再从业务逻辑的角度来看看这个接口都做了什么?这个接口有大量的写操作,可以从近10次的写操作中直观地看到一些端倪。通过上图中的时序,相信大家已经能看出一些问题了,咱们从业务和技术两个视角来一起看下这个接口:
业务视角
把上面的关键功能快速分类,可以分成两块:一块是更新维修项状态,以及由此触发的维修单状态和维修商品状态更新。另一块是根据更新后的状态,生成相关文档,并记录文档和维修项的关系。
技术视角
技术上的问题比较突出,重点总结为以下几点:
- 问题一: BFF承载了过多的业务。从上面的两大块业务功能来看,如果去掉这个BFF,后端是不具备完整的业务能力的。这正是我在《BFF避坑指南》中描述的一个BFF实施常见问题,将BFF实现成了企业级服务总线(ESB)。后端服务只需要具备必要的增删改查接口,BFF就可以完成前端的大部分需求。这样的BFF 以一己之力承载了前端所有的业务,最终一定会走向大泥球,成为架构中的瓶颈和脆弱点。
- 问题二: 后端服务接口设计不是面向业务能力,而是面向数据。在这个事例中,一个BFF接口中要调用3个维修单服务接口才能完成完整的逻辑,维修单服务并没有将完整的业务能力内化在自己的服务内,而是提供了一些可以组合的接口,这也是开发者将大量逻辑写到BFF中的众多原因之一,或者说是BFF这种实践思想的一种后果。
- 问题三: 后端服务间数据关系比较紧密,耦合较重。一个业务状态的更新会涉及三个后端服务数据的更新,这背后的坏味道就是服务间耦合较重,边界不清晰,会导致大量业务功能需要多个服务配合才能完成。
- 问题四: 一个接口完成多次写操作,很难保证数据一致性。任何一个写接口的失败都会中断整个流程,而中断点不同,产生的数据一致性问题也不一样,在不采用分布式事务的情况下,这个接口上线后一旦产生问题,都将是非常棘手的。
- 问题五: 接口逻辑过于复杂,很难保证性能,稳定性差。几十次的跨服务间调用,即便每个调用都是毫秒级响应,这个接口的性能最好也只有几百毫秒,稍有波动就会上升到秒级,甚至更差,很难保证线上的稳定性,在有网络波动或异常的情况下,用户体验极差。
BFF治理的原则与措施
上面的案例虽然不能覆盖BFF实践过程中的所有问题,但大部分典型的问题都暴露出来了。我们可以借着这个案例来讨论如何在实践中优化BFF的设计,达到治理的目的。
在给出改进方案之前,为了保证方向的正确性,我们先看下BFF的设计原则:
- BFF为前端而生,关注点在提升前端用户体验。
- BFF不承载业务能力,业务逻辑要下沉到合适的后端服务中。
- BFF不承载特定技术能力,必要时可以建立专门的服务承载,如文档打印、Excel生成、算法逻辑等。
- BFF不做后端服务的集成层,某个后端服务的数据变更需要同步到其他服务,不能通过BFF实现。
仍然以上面案例里的场景为例,经过上面的分析,关键的问题已经找到了,就可以对症下药了。还是分两个视角来看:
- 业务视角
从业务视角的优化显而易见,就是优化现有交互流程,将这一步操作改为两步,先完成业务数据的逻辑变更,再进行文档的逻辑处理。
技术视角
从分析中可以看到,BFF暴露出来的问题其实不仅仅是BFF自身的问题,也反映了后端服务的问题,因此要治理BFF,首先要对后端的服务进行优化治理,再调整BFF内部的问题:
- 从业务能力出发,重新审视后端服务之间的边界,必要时将耦合紧密的服务进行合并。
- 重构后端服务提供的接口,面向业务提供接口。比如案例中更新维修单状态后,需要记录维修历史,需要关闭维修单,这些逻辑都在维修单服务中通过逻辑的关联处理完成。
- 将BFF中的业务逻辑下沉到合适的后端服务中,后端服务之间要保持有效的通信机制,保证数据变更引发的连锁更新可以有序准确地完成,而不是由BFF作为集成媒介,在有写操作的场景下,BFF只调用具备这个业务能力的后端服务接口,由这个后端服务负责处理所有相关数据的更新。在案例中,维修项和维修单的状态更新,应该根据前面的业务能力分析,放到某一个后端服务中来控制,或者放到二者合并后的服务中,最后再由这个后端服务负责完成与商品服务之间的数据更新。
- 将BFF中的特定技术能力抽取成共享库,或者下沉建立服务。在案例中,生成文档的功能就可以建立一个单独的服务来处理,既解决BFF的问题,也能提供更好的复用能力。
在这些优化梳理和落地的过程中,一定还会发现其他相同或类似的问题,任何一个软件架构走到需要治理的地步都不是一个接口的问题,一定是一系列相互关联的问题,往往优化的过程中需要一起考虑,站在更高的视角来统一规划。
BFF治理的方向总结
在微服务架构下,BFF模式是非常必要的一种实践,特别是在微服务比较多的情况下,BFF分离了前后端,可以让前端和后端更专注在各自的领域。在软件架构中,边界的处理是最难的,BFF作为中间这一层,作用非同小可,其落地实践对架构的影响至关重要。BFF治理是微服务架构下的软件架构治理中非常重要的一部分,通过BFF治理也能发现很多前端或后端设计的问题。
经过上面案例的分析,总结一下BFF治理的主要方向:
- 首先分析BFF问题背后是不是存在后端服务设计问题,优先解决后端服务的设计问题。 从业务上分析BFF接口的职责,保证接口职责单一。
- 将BFF中业务能力下沉到后端服务。
- 将BFF中需要复用的技术能力抽取成共享库或下沉建立后端服务。
- 避免一个BFF接口依赖过多的后端服务,根据系统复杂度来看,最多依赖不超过5个后端服务为宜。
- 避免一个BFF接口多次写操作,不滥用BFF站在上帝视角所拥有的权利,各司其职。
BFF (Backends For Frontends)是为前端设计的后端,不可图一时之方便而越俎代庖,将大部分该由后端服务提供的能力据为己有,而更应该关注在前端的体验优化上,做好前后端的隔离,让前后端能够各司其职,合理高效协作。