微服务/API前端开发时代之 BFF 速学入门教程

  • Post author:
  • Post category:其他


什么是BFF(Backends For Frontends)

顾名思义,它是前端的后端(服务器)。专门为前端而调用API,或者生成 HTML 的服务器。看到这里你可能会想,“这与传统的Web应用服务器有什么不同?”。本质上是一样的,只是专门为前端打造这一点不同而已。

首先,Web应用服务器有如下几种用途:

  • 从数据库和全文搜索引擎等中间件获取和更新数据

  • 创建一个页面

  • 作为HTTP接口从用户那里获取输入信息

在这里,从数据库和全文搜索引擎中获取和更新数据的部分旨在进行管理,同时确保数据的完整性和可靠性。构建页面的部分和获取用户输入信息的部分对应于用户界面(UI),目的是提升用户体验(UX)。

前者作为后端(Backends),而后者作为前端(Frontends),通过前后端的划分让开发者专注于各自的专业领域,这样的架构设计被称为“BFF”。

轮廓图如下所示

114781fdca7b43aa8593613716dfc9ac.png

像这样,BFF往往采取“设置在反向代理和后端API服务器之间”的配置。反向代理是一个用来替代Web应用服务器,进行静态文件压缩和缓存的服务器。后端API服务器主要与数据库、全文搜索引擎等中间件配合,起到操作资源和管理数据的作用。

BFF负责UI/UX相关的功能,比如在这两个服务器之间建立一个页面,接受用户的输入信息并发送给后端。

BFF 产生的技术背景和历史背景

技术背景

在前端领域的快速发展状态下,前端的开发早已从MVC的传统模式转变为现如今的前后端分离架构。

MVC的开发模式中,前后端开发过分耦合,导致开发效率低下,分工不均。出于解耦的目的,提出了前后端分离的架构。前端通过AJAX调用后端接口进行交互,实现前后端项目分离。

49469efd6c688f622a23c96be9bd13db.png

随着前端领域的不断扩大,后端服务在复杂的前端业务背景下,为兼容不同的业务逻辑变得臃肿而难以维护。

在这样的背景下,后端微服务架构逐渐成熟,领域之间的解耦也成为后端服务的主流。然而,前端需要自身去实现数据的聚合、裁剪等功能。虽然我们可以通过前端去请求不同的服务然后做数据的操作,但是由于不同的生态下,会存在一定的限制,例如微信小程序的域名数量限制。并且,服务的底层协议也会存在一定的限制,如RPC协议。为此,BFF中间层架构是一个比较不错的选择。

BFF(Back-end for Front-end) – 服务于前端的后端。

934d87d9be53714a5eff17ee33ae1a77.png

历史背景

在21世纪初,使用 JavaScript 进行 HTTP 请求的Ajax 通信概念开始普及,Web 应用逐渐变得丰富,且具有更强的交互性。随着富Web应用数量的增加,以及更多的处理集中在了客户端,服务器端越来越多地使用了仅发送和接收数据的 API 。

2507555a3f2b224df6dd26a242137707.png

此外,随着除 Web 应用之外的客户端(例如移动应用)数量的增加,服务器端需要构建专注于某一个领域的 API。它已经演变为“专门处理特定资源的架构”,因为它被称为微服务。

bf7177a773d40831d1f7d86ccc91d381.png

但是,随着客户端的更加多样化,创建满足所有客户端需求的 API 服务器变得越来越困难。你创建的移动应用和 Web 应用,UI 也各不相同,不同的客户端上所需要展现的内容也可能不同。例如,你创建了一个 Web 应用,由于屏幕尺寸的不同,用户可以看到的信息在 PC 和智能手机上可能会有所不同,甚至 UI 可能与移动应用完全不同。

此外,Web 应用具有环境限制,例如在 HTTP/1.1 中可以同时请求的请求数限制为 6。

针对这些情况,出现了一种架构,将响应每个客户端请求的服务器放置在前端,充当与后端API服务器的桥梁。这是因为 BFF 具有构建 HTML 和减少请求数量等优点。

65dbd762864d416e0a423bf414f86b21.png

这样,一种叫做“BFF”的架构就诞生了。

前端工程师还是后端工程师,谁来负责?

BFF 通常由负责客户端的前端工程师开发。由于BFF是服务器,可能会认为会由后端工程师开发,但既然是帮助构建和操作UI的服务器,那就是前端工程师的职责范围.

在 BFF 架构中,后端工程师负责基于 API 管理资源。

084d066d8ab512d9bc007e8a899efb1a.png

BFF能做什么

既然BFF架构相对于基本的前后端分离能解决一些复杂的业务问题,但是具体能做什么?

构建SSR服务解决C端网站SEO和白屏问题

当我们需要开发一个C端项目(面向用户)的时候,页面的快速响应和搜索引擎优化(SEO) 是我们必须着手去解决的问题。但是SPA页面的特性会导致页面一定的白屏时间。SPA页面下源码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
   />
    <title><%= VUE_APP_TITLE %></title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

当用户打开页面时,页面展示白屏,通过AJAX请求后端服务加载数据后构建DOM,展示页面。

ad0244d0cb4c226fd8b33d4d1a5df470.png
同时,对于爬虫来说,获取的内容也仅仅是模版文件的内容。

解决

在BFF中间层中,我们可以用SSR(服务端渲染) 的方式去解决以上问题。具体实现参考官方文档

import express from 'express'
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'

const server = express()

server.get('/', (req, res) => {
  const app = createSSRApp({
    data: () => ({ count: 1 }),
    template: ``
  })

  renderToString(app).then((html) => {
    res.send(`
    <template></template>
    `)
  })
})

server.listen(3000)

04c0be0e8ef145a79e60116e80a1ebf0.png

在这个过程中,就不会存在等待AJAX请求的时间。同时,渲染的DOM结构是完整的HTML而不是仅是模版的HTML。

应对不同端对同一API的不同需求

我们可能存在这样的需求:应对不同的客户端开放不同的权限和功能,如小程序端仅支持阅读而不能添加评论,如果需要评论信息,只能在APP端进行。

面对这样的需求,微服务架构下的BE(后端开发)可能不会为了这样的功能单独实现。所以我们可以通过BFF层实现自己的业务逻辑判断,实现服务自治。

032c5cf7564f05f0c8f35a2bd6a26e9b.png

主要目的是对不同的客户端进行特定的业务处理;同时,集中处理统一逻辑,降低开发成本。

聚合不同服务API的数据

我们经常会遇到一个页面的初始化需要多个接口的数据,甚至是不同服务的数据。比如用户服务、订单服务等。

通常,这些服务以依赖的关系去请求,如订单服务需要依赖用户的信息。

d1958a247a121f1fb5ab3828a86050a5.png
使用BFF架构去优化网站:减少请求数,这样就可以在客户端只发送一个请求,由BFF层去做一些数据的融合和多服务请求的操作。

d8d9c388c3a1d0ee29e927acf57b9966.png
同时,在数据BFF返回数据的过程中,我们可以移除部分不需要的冗余数据。

定制自己的登陆和权限控制方案

最近在做一个B端的项目,项目有这样一个需求:根据不同的角色,实现接口访问控制。关于登陆的实现可参考之前的文章关于如何基于Google OAuth2.0 搭建系统鉴权

但是,对于服务提供方来说,我只是提供服务的,并不会对权限进行控制。所以,需要实现这个需求,只能自身从BFF层考虑。

实现

整体实现基于NestJS+Typescript。

定义一个装饰器

实现Nest自定义的装饰,用于Controller层队接口进行校验拦截。

import { SetMetadata } from '@nestjs/common'

export const Permissions = (...permissions: string[]) => SetMetadata('permissions', permissions)

实现守卫

守卫的目的就是实现拦截的手段。

async canActivate(context: ExecutionContext): Promise<boolean> {
    const roles: string[] = this.reflector.get('permissions', context.getHandler())
    if (!roles) {
      return true
    }
    // 获取请求Req
    const req = context.switchToHttp().getRequest<any>()
    const token = req.headers['Authorization'] || req.headers['authorization']
    if (!token) {
      throw new UnauthorizedException()
    }
    
    // 业务代码
    //
    //
    //
    
      return this.hasPermission(permissions || [], roles)
    }
  }

控制层使用

@Post()
  @Permissions('0101')
  async getBizData(@Body() data: QueryDto) {
    return this.myService.getBis(data)
  }

BFF所带来的问题

当一个架构能从一个方面带来了优势的同时,必定也会牺牲其他方面

资源问题

当BFF层多了,资源使用就成了问题,毕竟会多一个服务。尤其是使用SSR服务端渲染的时候,随着QPS(每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。)的波动,CPU的使用和内存总是会有比较高的波动。不过这方面考虑使用服务提供商的伸缩性服务。

维护问题

随着BFF层的使用,前端也不仅仅只是需要关注浏览器端的知识点。同时也需要掌握后端服务的知识,如数据库、服务器、并发问题等等。会使得前端的同学维护起来较为困难。

但是,也建议大家跳出自己的舒适圈。

服务链路问题

在引入BFF层之前,可能只是单纯的服务调用。服务之间的依赖在前端比较清晰。

在使用BFF层架构之后,流程变得繁琐,要同时走前端、服务端的研发流程,多端发布、互相依赖,导致流程繁琐。出现问题的时候,需要清晰的链路追踪,这里一定要将日志的链路完善好,避免造成问题排查的困难。

原文地址:https://juejin.cn/post/7008440419054780446#heading-3 https://juejin.cn/post/7065687335517552671#comment

源码

最后, 送人玫瑰,手留余香,觉得有收获的朋友可以点赞,关注一波 ,我们组建了高级前端交流群,如果您热爱技术,想一起讨论技术,交流进步,不管是面试题,工作中的问题,难点热点都可以在交流群交流,为了拿到大Offer,邀请您进群,

入群就送前端精选100本电子书以及 阿里面试前端精选资料

添加

下方小助手二维码或者扫描二维码

就可以进群。让我们一起学习进步.

5f23543c8a7ca473bfcfef868d5a0a0e.png


推荐阅读

(点击标题可跳转阅读)


[极客前沿]-你不知道的 React 18 新特性


[极客前沿]-写给前端的 K8s 上手指南


[极客前沿]-写给前端的Docker上手指南


[面试必问]-你不知道的 React Hooks 那些糟心事


[面试必问]-一文彻底搞懂 React 调度机制原理


[面试必问]-一文彻底搞懂 React 合成事件原理


[面试必问]-全网最简单的React Hooks源码解析


[面试必问]-一文掌握 Webpack 编译流程


[面试必问]-一文深度剖析 Axios 源码


[面试必问]-一文掌握JavaScript函数式编程重点


[面试必问]-阿里,网易,滴滴,头条等20家面试真题


[面试必问]-全网最全 React16.0-16.8 特性


总结


[架构分享]- 微前端qiankun+docker+nginx自动化部署



[架构分享]-石墨文档 Websocket 百万长连接技术实践


[自我提升]-Javascript条件逻辑设计重构


[自我提升]-送给React开发者十九条性能优化建议


[自我提升]-页面可视化工具的前世今生


[大前端之路]-连前端都看得懂的《Nginx 入门指南》


[软实力提升]-金三银四,如何写一份面试官心中的简历

觉得本文对你有帮助?请分享给更多人

关注「React中文社区」加星标,每天进步

c1e4ff7491ed47bb3b6be7a795f4df7a.png

点个

赞👍🏻

,顺便点个

在看

支持下我吧