企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
  BFF字面意思是服务于前端的后端,我的理解就是数据聚合层。我们组在维护一个后台管理系统,会频繁的与数据库交互。   过去为了增删改查会写大量的对应接口,并且还需要在Model、Service、Router三层写不同的代码逻辑,吃力不讨好。   为了节约开发时间,构思[通用接口](https://www.cnblogs.com/strick/p/15042209.html),并付诸于实际项目中。虽然简化了Router和Service部分,但其实就是将该部分的代码迁移到了前端页面中。   这里有一点小隐患,因为目前我们组的成员是全栈维护,所以知道数据库ORM的语法规则,若前后端分离,那就不可取了,并且工作量其实是从后端转移到了前端。   虽然时间是节约了一些,但是后端的代码却暴露在了前端,维护性方面下降了不少,于是想到了BFF。   首先查看了许多已经在运行的成功案例,有些是基于GraphQL重新封装的系统,有些是定制化的系统。经过三天的仔细权衡对比后,决定自己定制化。   主要考虑了两方面,一方面是改造成本,如果基于GraphQL的一些封装库(例如[Type-GraphQL](https://typegraphql.com/)、[apollo](https://www.apollographql.com/)、[Prisma](https://www.prisma.io/)等)来设计系统的话,那势必需要了解这些的库的方方面面,并且还需要将其集成到已经的结构中。   另一方面是使用成本,系统完成后是给人用的,不能太复杂,为了避免让使用人员来适应这套系统,最方便的就是将之前的开发流程修改成配置化。   BFF的实现逻辑由后端定义,并且⽆需重构,也不必后端配合改造与联调。   这套系统完成后,会真真切切地影响之前的开发流程,例如不必单独写接口文档,并且可以随时在系统中调试,而不必借助postman调试。 ## 一、前端界面 **1)配置**   当前80%的Node接口代码复杂度都并不⾼,基本都是机械化重复的,这些接口可分为三部分:参数处理(1)、服务调用(2)和响应聚合(3),类似于下图。 :-: ![](https://img.kancloud.cn/d3/37/d33774e6796f18603532ee43782dd0da_1382x412.png =600x)   那么前端界面就可以围绕这三部分展开,如下图所示,其中处理器就是服务调用,只是会基于通用接口服务和指定的Model的封装函数。 :-: ![](https://img.kancloud.cn/29/52/2952f0fad0ac5bc141922761a81875d3_2252x1913.png =800x)   权限ID就是一段字符串,会在权限系统做接口校验,具体会在后文讲解。模块其实就是之前Router目录中的各个文件,现在将它们作为选项存在。   参数是可以动态配置的,处理器也是一样,并且在选中方法后会显示方法名和方法参数,而在选好Model文件后,会出现查看按钮。 :-: ![](https://img.kancloud.cn/ea/d7/ead7a229727547e84abde4eda5b3fe0e_1344x434.png =600x)   点击查看按钮就能看到Model文件中映射的属性,以及数据库表的字段了,在之前的开发中经常需要查找这些属性和字段,甚是繁琐。   逻辑结构就是接口的主体,除了参数部分的代码不需要写之外,其余代码都在这里完成,是整个接口最为核心的部分。 :-: ![](https://img.kancloud.cn/50/4b/504b5ec3faa23d7ecbb052916ecf6361_1392x816.png =600x)   这部分的处理我其实考虑了很久,在简便和自定义之间找到了一个平衡点,最终才实现了上述效果。   之前声明的参数和处理器都可以在这个编辑器中引用,这个代码编辑器采用的是[monaco-editor](https://microsoft.github.io/monaco-editor/),微软出品的VS Code浏览器版本,该有的功能都有。   但是我只集成了代码高亮的功能,自动索引的功能没有成功集成进来,顺便说下,官方的API文档非常不友好。 **2)调试**   在配置化界面的最下方,就是调试部分,当接口创建完成后,就能马上调试。 :-: ![](https://img.kancloud.cn/e8/fa/e8fa0b15eb45a7407302d39effb22530_1564x1124.png =600x)   点击API文本框中的搜索icon,就能看到最终的源码了,能帮助自己迅速定位问题。 :-: ![](https://img.kancloud.cn/cf/29/cf2936bebc2981b9ff0b81e9851b74a3_1214x1336.png =600x) **3)列表**   在列表界面中,包含新建的入口,以及查看和编辑。当跳转到创建页面后,点击浏览器的返回键,列表页面能恢复成之前的样子。 :-: ![](https://img.kancloud.cn/2e/88/2e88c0d5343a11222d866ca41e05687b_1558x994.png =600x)   列表页面的状态不会受跳转的影响。点击查看会出现配置信息、源码和调试界面,这些配置信息就是接口文档,并且还能随时调试。 :-: ![](https://img.kancloud.cn/04/72/04722156e3f59ff12a76c994292bfc65_1216x1330.png =600x) ## 二、后端服务   这次我将API接口的数据都存储在MongoDB中,主要考虑的是数据中会包含大量的数组和JSON对象,若存在MySQL中,就需要在存入和取出时做序列化和反序列化。 **1)vm**   后端在接收到界面中的参数后,就会将相关参数解析成对应代码,再拼接成一整段的字符串代码。执行这段代码使用的是Node内置的[vm模块](http://nodejs.cn/api/vm.html)。 ~~~ const sandbox = { ctx, services, console, redis, mainFunc: function () {} //主函数 }; vm.createContext(sandbox); // 在执行上下文运行 vm.runInContext(code, sandbox); await sandbox.mainFunc(); ~~~   在sandbox变量中,特地声明了一个mainFunc属性,因为执行的代码中会使用await语法,那么就需要将代码包在由async声明的函数中。 **2)接口调用**   Node服务基于KOA框架,路由基于koa-router库,为了尽量与之前的调用方式保持一致,就重新声明了一个可配置的路由。 ~~~ router.all("/bff/:path1/:path2", async (ctx) => { const { path1, path2 } = ctx.params; const bff = await services.common.getOneBFF({ api: path1 + "/" + path2 }); if (!bff) { return; } //权限判断 if (bff.authority) { const pass = await checkAuth(ctx, bff.authority); if (!pass) return; } //运行代码 await runCode(bff.rawCode, ctx); }); ~~~   在这个方法中,配置了两个路径参数path1和path2,所有通过这套BFF系统创建的接口,在前端调用时,都需要添加 /bff/ 前缀。   而权限判断都会交由 checkAuth() 函数处理,之前这个函数是一个中间件,那么将其关键部分抽象出来后,也能达到权限验证的效果,与普通接口无异。 ***** > 原文出处: [博客园-Node.js躬行记](https://www.cnblogs.com/strick/category/1688575.html) [知乎专栏-Node.js躬行记](https://zhuanlan.zhihu.com/pwnode) 已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎阅读。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。