BFF (Backend for frontend)避坑指南
BFF —— Backend for frontend(服务于前端的后端),是为了让后端 API 满足不同的前端使用场景而演进出来的一种模式。这篇文章我们主要来看看在 BFF 的实施过程中可能遇到哪些“坑”,为了能够快速理解后面讲到的问题,我们先来简单回顾下 BFF 的由来和应用场景。
BFF 的由来
随着移动设备的快速发展以及产品对用户交互体验的关注度增强,前后端分离的架构模式也逐渐被大多数企业所采用。在这种模式下,统一的后端 API 很难满足在不同场景下对用户体验的不同需求。BFF 模式应运而生,一方面 BFF 隔离了因前端 UI 展示对后端 API 的需求,企业可以在后端构建核心业务能力,另一方面,BFF 可以根据已有的后端 API,快速满足前端在 UI 展示上的需求,来不断提升用户体验;
另外,从知识管理的角度,BFF 模式让知识边界定义的更清晰,后端专注于构建业务能力,不需要考虑前端各种场景适配的问题;而前端更关注用户体验,可以随时独立发布更新。
BFF 的应用场景
面向前端
BFF 为前端而生,随着前端技术(iOS、Android、小程序、Web 等)的不断发展,不同前端对后端要求有很大差异,后端服务很难提供满足多个前端的统一接口,BFF 则可以针对前端的特定需求,做出适配:
针对前端 UI 展示逻辑的不同,对后端 API 返回的数据进行裁剪和重新组织,提供面向前端的定制化格式的数据
根据前端业务需求,对后端多个 API 返回的数据进行聚合
对一些特定场景的数据进行缓存,提高性能,进而提升用户体验
面向后端
BFF 隔离了前端 UI 展示对后端 API 的定制化需求,可以很好地支持后端服务的演进:
从单体应用迁移到微服务架构,后端架构的演进可以通过 BFF 来进行隔离,参见《前后端分离演进:不能微服务,那就 BFF 隔离》。
微服务架构内部的演进,微服务的拆分,也可以借助 BFF 来减少对前端的影响,参见我的另外一篇文章章《迭代开发中的微服务拆分》。
BFF 实现中的坑
BFF 模式在前后端分离的架构模式下的确有很多好处,完美隔离了前后端,貌似从此以后前端干前端的,后端干后端的,不会再有冲突,一切是那么美好。然而事实并非如此,在实际实施 BFF 的过程中,大家仍然会遇到各种问题,下面介绍几个常见的实施 BFF 过程中的问题:
重复代码
通常情况下我们会为每个不同的前端构建一个 BFF,还可能会为一些特定的场景建立 BFF(如对第三方系统提供 API),不可避免的,多个 BFF 之间会出现大量的重复代码,比如可能会存在相同的数据转换逻辑,相同的 API 数据聚合逻辑等等。
重复代码通常被认为是一种坏味道,但在 BFF 这个场景下,这些重复的代码是为了某一个特定的前端服务的,因此处理这个重复要相对谨慎,如果一味粗暴地去掉这些重复,很有可能引发某一个前端需求变化会影响到其他前端应用的情况。
如果确实有些代码重复且相对稳定,可以尝试采取下面的方法来消除重复:
共享库,将重复的代码抽取出来放到共享库中,不同的 BFF 通过依赖共享库来达到代码复用,但也要考虑哪些代码能够抽取到共享库中,可以参考我之前的文章《消灭微服务的坏味道 之 共享库》。
将重复的代码放到后端服务中,这种方法适用于重复的代码实际承载了真实的业务逻辑,而不只是在做简单的数据聚合和数据格式转换,那么应该把这些重复的代码下沉到指定的后端服务中。
趋向于 ESB
除了大量的重复代码,在微服务架构下,另外一个问题通常会出现在业务演进过程中。当新的业务需求产生时,具体要在哪个后端服务中实现有时候不是一个很容易回答的问题,特别是不同的服务有不同的团队归属时,如果每个服务的归属团队都认为新的业务需求不是自己服务的业务范畴,最可能的结果就是让 BFF 负责帮忙组合各个服务的功能,完成这个新的业务需求。渐渐地,BFF 朝着 ESB 的方向发展,变成了集成各个微服务,对外提供新能力的中间件。
这种趋势某种程度上违反了 BFF 设计的初衷,BFF 为了适配前端而生,而真实的业务能力应该由后端服务来承接,而不是 BFF。
要解决这个问题,首先要清晰的定义每个服务的业务职责,为了避免服务过多很难划分清晰边界,最开始服务尽量不要拆的太小;另外要建立规则来处理新的需求,哪些需求属于 BFF 的范畴,哪些属于业务能力,业务能力应该下沉到后端服务中,如果现有服务无法承载这种能力,可以考虑新增服务,而不是写在 BFF 中。
性能问题
还有一个比较明显的问题就是性能问题,想象下面的场景(相信你一定遇到过),既然有了 BFF,前端的设计真的可以放飞自我了,可以把想展示的信息一股脑都放在一起展示,极端情况下 BFF 可以获取到任意服务的数据进行组合,我曾见过在一个 BFF 接口中调用了后端服务几十次来拼凑前端需要的数据,可想而知这个接口的性能一定不会好。
为了解决这种情况带来的性能问题,通常会采用并发获取数据然后拼装的方式,这明显增加了代码实现和维护的复杂度,往往会引发新的问题。
因此,在实际开发过程中要非常警惕这种问题的发生,如果一个 BFF 接口调用了 3 个以上接口,那就要分析下后端服务拆分的合不合理,前端 UI 展示的数据是否有必要放在一起,否则性能问题会逐渐成为一个不可避免的问题。
小结
架构设计是通过合理的组件拆分以及定义组件之间的关系,将系统整体的复杂性分散到不同的组件中,在更低的维度上解决问题,分而治之。BFF 在前后端分离的架构模式下隔离了前端和后端的关注点,特别是在多个前端或第三方的情况下,BFF 都是非常好的选择。然而在实际实现过程中,仍然要时刻警惕,明确 BFF 设计的初衷,避免因引入 BFF 而带来了更多的问题。
参考资料
[Pattern: Backends For Frontends] https://samnewman.io/patterns/architectural/bff/
[BFF @ SoundCloud] https://www.thoughtworks.com/insights/blog/bff-soundcloud
[Frontend Architectural Patterns: Backends-For-Frontends] https://medium.com/frontend-at-scale/frontend-architectural-patterns-backend-for-frontend-29679aba886c
版权声明: 本文为 InfoQ 作者【码猿外】的原创文章。
原文链接:【http://xie.infoq.cn/article/21b54b04b441231eeed0a14db】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论