世界实时:探索原味BFF模式
2022-09-14 16:02:04来源:Thoughtworks洞见
作者|黄逸偲
(相关资料图)
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从一个解决不同变化速率和需求的适配器,变成分布式单体的一个转化器。
我们往往会在系统设计之初犯下一个错误,那便是希望所有东西在一开始都是可复用的。这种思路会给系统后续的开发和维护带来巨大的挑战,挑战可能是来自应用间的协调,也可能是兼顾复用带来的高工作量。特别是在维护多个客户端或消费者的场景下会带来更大的困难。我们应该在考虑通用用法之前,先专注于功能和特定用例。在了解系统现状的主次和具体情况后,再针对性地区分需要通用和特殊处理的部分。这种系统设计和开发的思路和方式,使我们能够拥抱变化,立于演进的不败之地。