为什么要 BFF
小程序前端直接调后端微服务,常见两个问题:一个页面要拼好几个接口、来回慢;鉴权散落在每个接口里,改一处漏一处。BFF 就是夹在小程序和后端服务中间的那一层,专门为小程序服务。
BFF 不是万能中间层。它只做「给小程序适配」的事,别把业务逻辑再往里堆,不然就成了第二个后端。
判断要不要 BFF,看一个信号就行:前端是不是要拼好几个接口、鉴权是不是到处重复。两个都中,就值得加这一层。
什么时候不该上?项目还很简单、前端就两三个人,硬加一层反而多了运维负担。规模到了再加不迟。
BFF 干的是「适配」,不是「代替后端做业务」。
- 聚合把多个后端接口合并成一次请求。
- 鉴权登录校验统一在网关做。
- 限流保护后端,也防刷。
- 降级下游挂了别让整页崩。
接口聚合:一次拿全
一个首页要拿 banner、分类、商品列表,前端原来发三个请求。BFF 里并行调这三个后端接口,合并成一个返回,前端一次就拿全了。
| 做法 | 效果 |
|---|---|
| 前端直连多服务 | 多次往返,慢 |
| BFF 聚合 | 一次返回,快 |
| 字段裁剪 | 只要小程序用得上的字段 |
// BFF 一次并发拉多个后端,合并返回
const [banner, list] = await Promise.all([
api.getBanner(), api.getGoods()
]);
return { banner, list };
聚合的时候顺便做字段裁剪。后端为了通用性,一个接口返回几十个字段,小程序其实只用其中几个。BFF 把用不上的字段剔掉,流量小、解析也快。
聚合也别贪多。一次聚合七八个后端接口,任何一个慢就拖垮整次请求。一般两三个最合适,多了就该重新拆。
鉴权:收在网关
登录态校验放在 BFF 这一层统一做,后端服务就不用每个接口都查一次 token。前端只跟 BFF 打交道,token 的事它自己处理。
// BFF 中间件先校验登录态,再放行
if (!checkToken(req.token)) return { code: 401 };
后端服务只信任 BFF 转过来的内部请求,外部直接打后端的口子要封掉。
token 刷新也在 BFF 里处理。前端只管发请求,token 快过期了 BFF 自己续,前端不用知道这套机制。
权限粒度也在网关控。不同身份的用户能调哪些接口,在 BFF 配好,后端只管老老实实出服务。
限流与降级
后端扛不住的时候,BFF 先拦一刀:限流,别把流量直接灌到下游。再做降级:某个商品服务挂了,首页先出 banner 和分类,别整页白屏。
- 按用户、按接口设限流阈值。
- 下游超时要有兜底数据。
- 降级别悄悄,要打日志。
限流不是单纯挡人。对恶意刷接口的,限流是保护;对正常用户,降级是体验兜底。两个一起做,才既稳又不伤人。
降级要有预案。事先想好哪个服务挂了用什么兜底,别等挂了才现想,那时候手忙脚乱。
错误统一处理
错误码也在 BFF 里统一翻译。后端一百个错误码,前端只认 BFF 约定的那几个。这样后端怎么改错误码,前端都不受影响。
// 后端错误码映射成前端统一约定
return { code: 0, data, msg: ok ? "" : "系统繁忙" };
统一的好处是前端好处理,坏处是映射表要维护清楚,别越积越乱。
接口契约最好有个文档。BFF 给前端的返回长什么样写清楚,前后端对接省得每次来回问;契约定下来,两边都照它来,出问题也好对。
常见 BFF 坑
| 坑 | 后果 | 应对 |
|---|---|---|
| 业务逻辑塞进 BFF | 变成第二个后端 | 只做适配聚合 |
| 不做降级 | 下游挂整页崩 | 兜底数据 |
| 后端口子没封 | 绕过 BFF | 只信任内部请求 |
BFF 本身也要高可用。它要是挂了,前端所有请求都断了,等于把鸡蛋放一个篮子里,得做冗余。
还有:BFF 改动也要走测试。它适配错了,前端和后端两头都跟着遭殃。
BFF 落地清单
- 把多接口合并成一次聚合请求。
- 鉴权统一收在网关中间件。
- 配好限流和降级兜底。
- 错误码在 BFF 统一翻译。
- 封住后端的外部直连口子。
BFF 让前端轻、后端稳,它自己别变重。
另外,BFF 上线本身也要走灰度,别一上来就把全部流量切过去。先小比例切,观察几天接口成功率,稳了再全量。
这层适配做透了,前端就能专心写界面,不用再惦记后端那些杂事。
这就是 BFF 最实在的价值所在。