当前位置: 首页 > news >正文

探索原味BFF模式

BFF — Backend For Frontends,经典分布式架构设计模式之一。我在学习和工作经验累积中,逐渐加深了对 BFF 的理解。作为一种模式,它具有一些更加确切的使用场景,和一些能匹配的特定问题。

在本篇文章中,你们会与我一起穿越回BFF诞生的历史中,寻找其起源。并一同探索和学习这个在分布式系统中出镜率极高的架构模式。

寻找历史的线头

在毫无头绪的情况下,我们可以首先从Thoughtworks技术雷达中 BFF 的条目入手,去找到一些历史的蛛丝马迹。BFF 条目的发布时间是在 2015 年 11 月10 日。从这个信息我们可以获知,BFF 在历史崭露头角应该是在 2015 年 。

紧接着,在谷歌搜索关键字Backend for Frontends 以及将时间范围限定在 2015 年 1 月 1 日到 2015 年 11 月 10 日。通过对比搜索结果的时间,我们可以轻易发现最早出现 Backend for Frontends 词条的文章。文中提到,BFF 这个名字是由当时团队 Tech Leader Nick Fisher首次提出,通过投票获得了内部团队的认可。好了,我们现在获得了一个非常具体的证据。作为严谨的技术工作者,我们找到其他的交叉证据,提高这个结论的置信度。

非常幸运的是,在另一篇2015 年的 Thoughtworks 洞见文章中也提到了与上面证据相同的内容。终于,我们可以说 BFF 模式是在解决 SoundCloud的分布式系统问题中首次出现。下面,让我们一起回到BFF第一次发挥威力的现场吧。

神功初成

为了能让大家更容易了解到SoundCloud 当年究竟遇到了什么样的挑战,我会在下面通过分类分项来列举情况以及进行分析。

背景:

  • SoundCloud主要是通过付费订阅与广告进行盈利(也就是说,越多的曝光渠道,会给SoundCloud 带来更多的盈利)
  • SoundCloud 是一个单体系统,通过暴露共享 API 的方式为 Web 客户端、Android 和 iOS 应用程序以及互联网、合作伙伴等渠道提供服务。这些共享 API 随着功能和特性一起增长,最终变成了平台与客户端之间的集成点。
  • 将 2007 年开始运行的 SoundCloud 从单体模式转变至微服务模式, 这里是具体改造过程。此时,单体服务已经被拆分为多个微服务。
  • 支持在 iOS 平台上新增的应用程序(原来的产品主要是在 Web 端提供服务)

主要动机:

  • 减少产品发布上线的时间
  • 支持 iOS 平台新的应用程序,隔离新用户体验设计带来的风险。
  • 增加后端团队与客户端团队合作的节奏,提高工作效率。

挑战:

  • 为了让第三方开发人员能更自由地集成,需要 API 设计不对数据的使用方式做出任何假设。 所以,为了提供简单的体验,也需要许多不同的 HTTP API 提供具有高数据宽容度的服务。最终,获取构建一个简单的页面的数据,也需要上百个 API 请求。
  • 当团队需要变更现有 API 时,需要确保不会破坏现有的任何客户端以及重要的第三方集成。所以,一旦需要添加新内容,都必须投入巨大工作量来确保新功能不只适用于特定客户端。 上面这些情况使协调日常工作变得更加困难,最终导致了新功能发布缓慢。
  • 开始准备开发新 iOS 应用程序, 新平台上应用程序的用户体验会全部被重塑

通过分析上面的各种情况,可以得出当时SoundCloud 后端团队面对如下几个问题:

  • 问题一:需要为第三方客户提供合适粒度的 API,结果提供的 API 数据粒度过细,导致想完成一个业务服务需要请求的 API 太多。
  • 问题二:对外 API 与特定的使用方耦合严重,边界模糊,复杂度高导致维护 API 的工作量巨大,新功能发布缓慢。
  • 问题三:iOS 平台新客户端改 进了用户体验和交互方式,需要隔离新App带来的风险,并且还要找到与多个客户端团队更好的合作方式。

这三个问题在后端团队进行微服务改造中往往也会遇到。让我们一起看看,当年的 SoundCloud 团队在面临同样的问题时,是如何一步步见招拆招,摸索出 BFF模式 这个内功心法的。

演进之路

接下来,BFF 模式演进这一分是由客户端团队获得的。由于他们是 API 的消费者, 可以将不同服务进行多次逻辑调用,混合到后端的用户配置(UserProfile)文件中。这样避免了对后端服务多次不同的调用,实现客户端对单个资源的简单请求。这将简化客户端代码并提高整体性能,例如:

  • GET /user-profile/123.json

后端团队接受了这个逻辑,并开始试验这个方式。他们在 BFF 中编写了很多 Presentation Model。 在完成一部分任务后,后端团队突然意识到 BFF 不只是被客户端使用的 API ,它本身就是申请的一部分。BFF 新的形态出现了,具体如下图所示:

随着时间推移,SoundCloud 的 BFF 也在增加。他们已经在生产环境同时维护着 5 个 BFF 了。为了进一步提高生产力,减少不必要的重复。 用户配置(User Profile) 被从每个不同的微服务中抽取出来,变成一个独立的在 Services 与 BFF 之间的应用服务(Application Service)。

SoundCloud 的 BFF 依然随着时间在横向增长,不同的是这种横向增长不会再引起任何问题了。最终,BFF 模式的架构演变成与我们现在使用的几乎一致了。架构如下图:

总结

我们在维护和使用分布式架构,同时面对多客户端时,BFF 模式提供了一种很好的架构模式,使后端团队在构建面向客户端的复杂需求时,能够掌控自己的命运。 并且,这种自主性对于快速迭代的客户端应用程序,能够提供快速而良好的体验。 通过支持持续的演进和变化,这种模式可以将相同变化趋势的消费者行为,限制在一个可控范围内。使他们变得更容易合作和改变,并且更好满足不同客户端的特性需求。

在系统架构中,因为离需求频繁变化的前端比较近(网络和组织架构上),BFF很容易野蛮生长,成为各种“妥协”的自留地,在使用的过程中,我们需要明确架构中各层相关的职能和边界。同时,如果确实有不得不去做的一些“妥协”,我们也一定要用技术债的方式,继续跟踪和管理,避免“妥协”越来越多以后,BFF从一个解决不同变化速率和需求的适配器,变成分布式单体的一个转化器。

我们往往会在系统设计之初犯下一个错误,那便是希望所有东西在一开始都是可复用的。这种思路会给系统后续的开发和维护带来巨大的挑战,挑战可能是来自应用间的协调,也可能是兼顾复用带来的高工作量。特别是在维护多个客户端或消费者的场景下会带来更大的困难。我们应该在考虑通用用法之前,先专注于功能和特定用例。在了解系统现状的主次和具体情况后,再针对性地区分需要通用和特殊处理的部分。这种系统设计和开发的思路和方式,使我们能够拥抱变化,立于演进的不败之地。

参考文献

  • https://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html
  • https://philcalcado.com/2015/09/08/how_we_ended_up_with_microservices.html
  • https://samnewman.io/patterns/architectural/bff/
  • https://martinfowler.com/articles/micro-frontends.html
  • https://www.thoughtworks.com/insights/blog/bff-soundcloud

文/Thoughtworks 黄逸偲
原文链接:BFF的由来-Thoughtworks洞见

相关文章:

  • unity初学 Mstudio教程
  • Complete Partition Of Array
  • 单节点k8s—自签名证书—四层负载均衡—helm安装rancher
  • 高频面试题:谈谈你对 Spring Boot 自动装配机制的理解
  • Apple Xcode 14 (14A309) 正式版发布(含下载)
  • mysql 执行计划 type详解
  • Java进阶篇之泛型
  • 发送post请求渲染el-table,并实现搜索和分页功能
  • [RK3568 Android11] 时间同步机制
  • 6、Mybatis-Plus wrapper的使用
  • 基于Web的爬虫系统设计与实现
  • Kubernets---配置 Pod 使用投射卷作存储
  • springcloud和分布式微服务学习笔记
  • 【“在路上”疫情信息检测】
  • `算法知识` 模意义下的乘法逆元
  • HomeBrew常规使用教程
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • Java,console输出实时的转向GUI textbox
  • JavaScript中的对象个人分享
  • js中forEach回调同异步问题
  • js中的正则表达式入门
  • leetcode讲解--894. All Possible Full Binary Trees
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 从输入URL到页面加载发生了什么
  • 从伪并行的 Python 多线程说起
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 诡异!React stopPropagation失灵
  • 移动端 h5开发相关内容总结(三)
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ​Linux·i2c驱动架构​
  • #HarmonyOS:基础语法
  • ${factoryList }后面有空格不影响
  • (3)(3.5) 遥测无线电区域条例
  • (bean配置类的注解开发)学习Spring的第十三天
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第6节 (嵌套的Finally代码块)
  • (LeetCode) T14. Longest Common Prefix
  • (安卓)跳转应用市场APP详情页的方式
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • (十三)Flask之特殊装饰器详解
  • (轉)JSON.stringify 语法实例讲解
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .net core控制台应用程序初识
  • .net FrameWork简介,数组,枚举
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .net 验证控件和javaScript的冲突问题
  • .Net 中的反射(动态创建类型实例) - Part.4(转自http://www.tracefact.net/CLR-and-Framework/Reflection-Part4.aspx)...