Featured image of post 去年的一点小工作(1):从BFF谈起

去年的一点小工作(1):从BFF谈起

前年底,之前创业的公司关掉正式回归了打工生活。现在在一家SaaS公司做一些新业务开发和架构的工作。这个系列的文章也是想整理一下2022年的一些小成果,有一些内容和相关背景可能因为各种原因无法描述更细节的内容,也请各位见谅。

后端和前端的配合,我们遇到了什么问题?

后端和前端配合的问题一直是一个在业界比较常见的问题。

对于一个创业公司而言,在业务初期的时候最开始可能只是一个简单的Web应用,从一个小体量的单体应用开始可能并不会有太多问题。但是当业务开始快速发展开始,一切就变得不同了。因为你可能会出iOS/Android App,甚至会有嵌入式终端设备。

monolithic-app

对公司而言,为不同的平台提供不同的接口是一件十分浪费成本的事,对后端而言肯定会希望对大前端(Web,App,etc.)提供一套完整的API支持不同的前端平台。然而事情并不是全部这么简单:因为前端平台限制,实现,可能会有不同的区别和业务逻辑设计,因此有些时候对接口的需求并不相同。事情也不仅仅是这么简单:如何管理你的API定义和变更,如何保证前端和后端的开发进度协调等等会有很多其他的问题。对我们的系统而言,更多的逻辑和细节是下沉在后端系统中的,前端的开发进度和排期往往是相差一个Sprint的,这会导致一些额外的延迟和沟通成本。

你可以看到这里我们遇到的问题:不必要的沟通成本和互相等待;后端团队开始对单体应用产生了问题,因为增加新的功能变得更加困难,同时救火的问题也变得更加突出。换句话说,微服务对于一个十几个不同的团队、上百人的开发而言绝对是一件收益更大的事情,但是在这个过程中,我们后端服务引入了BFF的概念。

什么是BFF以及为什么我们需要它?

BFF (Backend for Frontend) 是一种软件架构模式,用于为前端应用程序提供统一的后端服务。BFF 模式的目的是为了解决前端应用程序与后端服务之间的耦合问题,使特定于应用程序的 API 更易于编写和维护。在传统的架构中,前端应用程序通常会直接与后端服务通信,例如 RESTful API。这样会导致前端应用程序与后端服务之间的紧密耦合,导致修改或更新后端服务时需要同时修改前端应用程序。

BFF 模式通过在前端应用程序和后端服务之间添加一个中间层来解决这个问题。这个中间层称为 BFF,它负责与后端服务通信并将结果转换为前端应用程序可以使用的格式。这样,前端应用程序就与 BFF 而不是后端服务直接通信,减少了耦合。这个模式最初是由 SoundCloud 在2015年引入的,当时他们需要为移动应用程序和网页应用程序提供不同的后端服务,但又不希望对后端服务进行大量修改。BFF 模式使他们能够在不修改后端服务的情况下为不同的前端应用程序提供不同的后端服务。

BFF

现在 BFF 模式已经被广泛使用,尤其是在微服务架构中,其中有多个后端服务需要被不同的前端应用程序访问。 BFF 允许前端团队对后端服务进行自定义,而不需要对后端服务进行修改。同时,BFF 也可以用来实现一些附加功能,如鉴权和授权,或者提供缓存和负载均衡。

request

BFF还有一个比较核心的想法是希望前端人员开发自治。当然这个在实际落地中必然存在的问题:前端会把这个作为前端的后端,导致堆压部分前端推过来的多余功能,甚至导致额外的CPU和内存压力瓶颈。这需要架构师在前端自治与需要谨慎管理的额外维护和运营开销之间足够的权衡。除了针对分布式环境及其复杂性进行优化之外,还需要前端和后端工程师之间的广泛协作,以确保前端开发人员使用的最佳 API 设计。

BFF的实现

其实从上面的概念可以看出,BFF的实践相信很多人都会发现其实国内也会有,不过国内更多的是API网关的实现。

BFF的实现其实和网关的概念紧密绑定,简单来说,API网关在请求进入网关后,会根据请求的路径和方法等信息转发到指定的服务上,也会对请求做一些认证和授权检查等。同时,API网关也可以实现负载均衡、分流、限流和缓存等特性,这样一来,前端就可以直接向网关发起请求,而不需要关心底层的微服务如何拆分和部署等问题,基本上可以说BFF模式的实现依赖API网关的技术。

GraphQL与BFF

但是除了API网关外,GraphQL也是一个BFF的选择。

GraphQL 是一种用于 API 的查询语言和运行时。它允许客户端在单个请求中请求所需的所有数据,并在响应中接收到所有所需的数据。GraphQL 的主要特点是它提供了一种灵活的查询语法,允许客户端指定所需数据的类型和结构。这与传统的 RESTful API 不同,后者通常需要多个不同的端点来获取所需的数据。

GraphQL 还提供了一种类型系统,用于验证查询是否有效,并捕获在运行时出现的错误。

GraphQL 可以在任何语言中使用,因为它是一种协议,而不是实现。

在我们的项目中,统一使用GraphQL对前端应用提供统一接口。这对于多业务线多业务场景下有更好的适配性,当某业务线需要接入另外业务线服务时,只需要同时加入对应的Query即可。

对我们而言,选择GraphQL对比RESTful API的优点更加适合我们的场景:

  1. 灵活性: GraphQL 允许客户端指定所需数据的类型和结构,而不是服务器提供的固定的数据结构。这样就能够避免返回不必要的数据,提高网络带宽的利用率。我们的每种前端需求各不相同,可以根据需求获取数据。
  2. 更少的网络请求: RESTful API 通常需要多个不同的端点来获取所需的数据,而 GraphQL 可以在一个请求中获取多个资源。这样可以减少网络请求的次数,提高性能。
  3. 可扩展性: GraphQL 的类型系统可以很好地支持未来的可能变化,因为客户端可以指定所需的数据,而不是服务器提供的固定的端点。
  4. 强类型:GraphQL有一个明确的类型系统,能够在编译阶段或运行时检查查询的有效性,更容易捕获错误。
  5. 更好的开发体验: GraphQL 通过提供一种灵活的查询语法,使得前端开发人员能够更轻松地访问和管理数据。

这里面对我们比较重要的还是1和2:我们的用户由于基建的原因,很有可能网络状况并不是很好,这和国内常见的情况有不少的差距。

同样的,GraphQL同样也有比较明显的劣势:

  1. 更高的复杂性: GraphQL 的查询语法比 RESTful API 更加复杂,需要更高的学习曲线。我们的工程师很多更熟悉RESTful API,但是对GraphQL需要重新学习。
  2. 更大的开发成本: 因为 GraphQL 查询语法更加复杂,所以在实现和维护时需要更大的开发成本。这部分不仅仅是来自于代码编写过程中,同时也来自于Code Review等等流程中。这些会在后面系列中额外说明。
  3. 性能问题:在一些场景中,例如大量的查询或较大的数据集,GraphQL 的性能可能会比 RESTful API 差。由于需要额外的数据转换,无论如何优化,额外的性能开销总是存在的。

不过这同样是架构上的取舍决策,更多的是根据你的场景选择对应的是实践方案。

然后呢?

其实这篇文章主要是在补充部分背景信息,还没有触及到去年工作的问题。不过仍旧絮絮叨叨的聊了这么多。这也是为了方便讨论后面的问题和实现。Stay tuned!

Built with Hugo
主题 StackJimmy 设计