BFF在SoundCloud中的实践(翻译)

2019/04/28

说明

这是一篇我翻译的文章,原文出处是:

https://www.thoughtworks.com/insights/blog/bff-soundcloud

翻译的原因无他,就是觉得文章写得好。

名词解释

  • BFF,backends for frontends,它是一种架构技术,一般指在微服务架构中根据客户端的不同,对后端接口进行分类支撑

  • SoundCloud,音频分享服务平台,是一个为喜欢音乐和爱好声音记录生活的人士打造的分享平台,用户可以上传音乐和音频文件,通过音频时间轴标记留言并分享

  • Rails, 是一个使用 Ruby 语言写的开源 Web 开发框架

  • monolith,单体架构,相对于微服务架构

  • Finagle,一种微服务框架

  • strangler pattern,一种处理大型 Web 应用程序中的重构代码发布方式。Web 应用程序是通过将功能映射到业务领域的不同方面的 URL 来构建的 — 将应用程序拆分为不同的功能领域,并一次一个领域地将这些领域替换为基于微服务的新实现。

正文

我们几年前在SoundCloud的朋友开始了一次把他们的单体架构Rails应用微服务化的旅程。在该项目中,对他们来说特别有效的一件事是BFF模式,也就是backends for frontends。 他们在BFF的实践和背后的成功故事可能有助于其他团队; 所以我们决定把它写下来。 我们希望您觉得它很有用。

为什么改造

早期,SoundCloud是一个单体架构系统,公开了一套服务于Web客户端,Android和iOS应用程序以及互联网的API,里面各种混搭,协作等。这个共享API随着每个功能需求而不断增加,因为架构的原因这些改动贯穿整个平台和API的使用者。

image

一段时间后,这种架构开始出现问题,无论是在添加新功能所需的时间方面,还是由于平台的不同需求。 例如,对于移动API,应该比web API更小的数据和请求频次。现有的单体架构API没有考虑到这一点,因为是另一个团队开发的,他们并不知道移动端的需求。 因此,每当应用程序增加一个新的访问点,首先前端团队要让后端确信这是真的需求,然后就是没完没了的沟通,开发的故事了。

问题很明显:不必要的摩擦,沟通开销和延迟。 与此同时,后端团队开始遇到单体架构带来的问题,因为添加新功能变得更加困难,同时bug修复的工作越来越多。

改造方案

为了解决这些问题,SoundCloud决定每当实现一个功能需要一个特定的,经过微调的API时,驱动它的团队也能够实现API端点,从而决定从哪里获取数据以及如何聚合它,什么有效载荷 需要传输等。此API端点将在整体应用程序之外创建,并充当特定于功能的接口。前端团队是这一层接口的所有者。

BFF诞生了,可以根据特定平台和功能(Android,iOS客户端,Web API和其他使用者)的需求进行调整。

image

前端开发者编写后端代码

这种转变意味着到目前为止主要依靠Android或iOS开发的主要专业知识来处理前端代码的人需要涉足后端:不同的技术栈和不同的挑战。 为了解决这个问题,后端团队开发了一个轻量级库,可以更轻松地编写“边缘服务”,这种服务是负责警报,监控,遥测,身份验证以及应用速率限制和清理传入请求的最佳实践。 这有助于标准化BFF的编写方式,因为它基于BFF框架更容易被使用(基于Finagle),而不是从头开始做所有事情。

BFF 作为迁移的一部分

BFF的一个特点是它消除了外部到下游服务的任何直接调用。此模式的实现还实现了向基于微服务的体系结构的最小侵入式迁移,因为BFF隐藏了应用程序本身的任何潜在变化。新功能通过BFF成为新的微服务或整体。每当从整体中提取更多功能时,BFF就会被更改,而应用程序代码保持不变。人们可以看到与strangler模式的相似性,其中BFF正在扼杀单体架构提供的公共API。因此,引入了名为public-api-strangler的BFF来隐藏消费者的整体API。它最初只是将请求转发给整体的代理。随着时间的推移,来自整体的部件被提取出来,而public-api-strangler只是将请求重新路由到新的微服务。消除对整体API的直接消费者调用,简化了向基于微服务的系统的迁移过程。

image

一个或者多个BFF

你应该选择多少个BFF? 在移动平台上,由于时间和资源有限,SoundCloud一开始iOS和Android使用一个BFF。 不过事后看来,为每个平台提供一个BFF会更好,因为Android和iOS应用程序的不同足以暴露不同的API需求。 您还可以考虑为每个平台提供一个BFF,如果两个平台上都存在特定功能,则可以使用相同的BFF。 SoundCloud用户可以分为创作者和听众; 因此,为创作者提供一个BFF,为听众提供一个BFF也是有意义的,因为这些用户组也可以获得单独的应用程序。 这是SoundCloud决定执行的思路。

人们可以尝试定义更细粒度的BFF,封装特定的功能,这将在BFF的发布周期上有更大的灵活性。但是,由于平台审核流程以及用户实际触发更新的意愿,应用程序发布周期更加“严格”,因此仍然与他们正在服务的应用程序紧密耦合,这部分是有争议的。

SoundCloud决定为每个上游用户创建一个BFF:它可能是一个服务于特定需求的应用程序(例如iOS监听器应用程序与iOS创建者应用程序)或用于嵌入式使用的API或面向Facebook,Twitter等战略合作伙伴的API。 BFF为不同需求带来的好处,特别是发布周期,足以证明这些努力的合理性。

目前,所有BFF都基于相同的核心库/框架(已在上面提到),每周发布时间表中更新。 共享BFF也有它的问题,例如:

  1. 过快地向库中添加新功能,而不是让它稳定应用在某些服务中,可能导致所有BFF升级到错误或不稳定接口的版本

  2. 允许足够的灵活性和可配置性,会让我们错误的认为默认实现适用于每个BFF

  3. 保持库尽量小并且可组合,因此开发人员可以根据自己的喜好选择部件

BFF的演化

现在SoundCloud的BFF显然随着时间的推移而增长。应用程序获得了新功能,它要求新的BFF功能。这种横向增长不会引起任何问题。但是BFF也在深度(垂直)上增长,并且暴露了一些重复的功能被提取到核心库中。 问题是哪些应该成为一个新的服务或库。指导规则是:如果要提取的功能不需要随时更新,则使用共享库,否则使用服务。

image

说到重复和复用,不要试图从一开始就使一切都变得通用。如果在整个组织中尝试这样,它将导致矛盾,因为很多人都想要贡献自己的想法。 考虑通用之前,请专注于您的功能和特定用例。 “通用的特殊功能优先”战略的效果要好得多。 在考虑提取代码时,不要忘记有用的“三规则”。

结论

BFF提供了一种很好的方法,使团队能够构建不同的面向客户端的应用程序来分而治之。这种自主权对于快速迭代客户端应用程序并快速提供良好的体验至关重要。 通过灵活的可变性,BFF支持快速迭代,并将整个系统变得更好,更低耦合,最终成为一套健壮的具有单一功能的API。

最后但并非最不重要的是,向SoundCloud的Kristof Adriaenssens,MatthiasKäppler和Michael England致敬,感谢BFF的故事以及对SoundCloud在微服务旅程中所有知识的分享。


欢迎关注我的公众号

(转载本站文章请注明作者和出处 犀牛饲养员

Show Disqus Comments

Post Directory