什么是BFF(Backends For Frontends)
顾名思义,它是前端的后端(服务器)。专门为前端而调用API,或者生成 HTML 的服务器。看到这里你可能会想,“这与传统的Web应用服务器有什么不同?”。本质上是一样的,只是专门为前端打造这一点不同而已。
首先,Web应用服务器有如下几种用途:
-
从数据库和全文搜索引擎等中间件获取和更新数据 -
创建一个页面 -
作为HTTP接口从用户那里获取输入信息
在这里,从数据库和全文搜索引擎中获取和更新数据的部分旨在进行管理,同时确保数据的完整性和可靠性。构建页面的部分和获取用户输入信息的部分对应于用户界面(UI),目的是提升用户体验(UX)。
前者作为后端(Backends),而后者作为前端(Frontends),通过前后端的划分让开发者专注于各自的专业领域,这样的架构设计被称为“BFF”。
轮廓图如下所示
像这样,BFF往往采取“设置在反向代理和后端API服务器之间”的配置。反向代理是一个用来替代Web应用服务器,进行静态文件压缩和缓存的服务器。后端API服务器主要与数据库、全文搜索引擎等中间件配合,起到操作资源和管理数据的作用。
BFF负责UI/UX相关的功能,比如在这两个服务器之间建立一个页面,接受用户的输入信息并发送给后端。
BFF 产生的技术背景和历史背景
技术背景
在前端领域的快速发展状态下,前端的开发早已从MVC的传统模式转变为现如今的前后端分离架构。
MVC的开发模式中,前后端开发过分耦合,导致开发效率低下,分工不均。出于解耦的目的,提出了前后端分离的架构。前端通过AJAX调用后端接口进行交互,实现前后端项目分离。
随着前端领域的不断扩大,后端服务在复杂的前端业务背景下,为兼容不同的业务逻辑变得臃肿而难以维护。
在这样的背景下,后端微服务架构逐渐成熟,领域之间的解耦也成为后端服务的主流。然而,前端需要自身去实现数据的聚合、裁剪等功能。虽然我们可以通过前端去请求不同的服务然后做数据的操作,但是由于不同的生态下,会存在一定的限制,例如微信小程序的域名数量限制。并且,服务的底层协议也会存在一定的限制,如RPC协议。为此,BFF中间层架构是一个比较不错的选择。
BFF(Back-end for Front-end) - 服务于前端的后端。
历史背景
在21世纪初,使用 JavaScript 进行 HTTP 请求的Ajax 通信概念开始普及,Web 应用逐渐变得丰富,且具有更强的交互性。随着富Web应用数量的增加,以及更多的处理集中在了客户端,服务器端越来越多地使用了仅发送和接收数据的 API 。
此外,随着除 Web 应用之外的客户端(例如移动应用)数量的增加,服务器端需要构建专注于某一个领域的 API。它已经演变为“专门处理特定资源的架构”,因为它被称为微服务。
但是,随着客户端的更加多样化,创建满足所有客户端需求的 API 服务器变得越来越困难。你创建的移动应用和 Web 应用,UI 也各不相同,不同的客户端上所需要展现的内容也可能不同。例如,你创建了一个 Web 应用,由于屏幕尺寸的不同,用户可以看到的信息在 PC 和智能手机上可能会有所不同,甚至 UI 可能与移动应用完全不同。
此外,Web 应用具有环境限制,例如在 HTTP/1.1 中可以同时请求的请求数限制为 6。
针对这些情况,出现了一种架构,将响应每个客户端请求的服务器放置在前端,充当与后端API服务器的桥梁。这是因为 BFF 具有构建 HTML 和减少请求数量等优点。
这样,一种叫做“BFF”的架构就诞生了。
前端工程师还是后端工程师,谁来负责?
BFF 通常由负责客户端的前端工程师开发。由于BFF是服务器,可能会认为会由后端工程师开发,但既然是帮助构建和操作UI的服务器,那就是前端工程师的职责范围.
在 BFF 架构中,后端工程师负责基于 API 管理资源。
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,展示页面。
同时,对于爬虫来说,获取的内容也仅仅是模版文件的内容。
解决
在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)
在这个过程中,就不会存在等待AJAX请求的时间。同时,渲染的DOM结构是完整的HTML而不是仅是模版的HTML。
应对不同端对同一API的不同需求
我们可能存在这样的需求:应对不同的客户端开放不同的权限和功能,如小程序端仅支持阅读而不能添加评论,如果需要评论信息,只能在APP端进行。
面对这样的需求,微服务架构下的BE(后端开发)可能不会为了这样的功能单独实现。所以我们可以通过BFF层实现自己的业务逻辑判断,实现服务自治。
主要目的是对不同的客户端进行特定的业务处理;同时,集中处理统一逻辑,降低开发成本。
聚合不同服务API的数据
我们经常会遇到一个页面的初始化需要多个接口的数据,甚至是不同服务的数据。比如用户服务、订单服务等。
通常,这些服务以依赖的关系去请求,如订单服务需要依赖用户的信息。
使用BFF架构去优化网站:减少请求数,这样就可以在客户端只发送一个请求,由BFF层去做一些数据的融合和多服务请求的操作。
同时,在数据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本电子书以及 阿里面试前端精选资料 添加 下方小助手二维码或者扫描二维码 就可以进群。让我们一起学习进步.
推荐阅读
(点击标题可跳转阅读)
[面试必问]-你不知道的 React Hooks 那些糟心事
[面试必问]-全网最全 React16.0-16.8 特性总结
[架构分享]- 微前端qiankun+docker+nginx自动化部署
觉得本文对你有帮助?请分享给更多人
关注「React中文社区」加星标,每天进步
点个赞??,顺便点个 在看 支持下我吧
文章评论