Node.js躬行记(12)——BFF
BFF字面意思是服务于前端的后端,我的理解就是数据聚合层。我们组在维护一个后台管理系统,会频繁的与数据库交互。
过去为了增删改查会写大量的对应接口,并且还需要在Model、Service、Router三层写不同的代码逻辑,吃力不讨好。
为了节约开发时间,构思通用接口,并付诸于实际项目中。虽然简化了Router和Service部分,但其实就是将该部分的代码迁移到了前端页面中。
这里有一点小隐患,因为目前我们组的成员是全栈维护,所以知道数据库ORM的语法规则,若前后端分离,那就不可取了,并且工作量其实是从后端转移到了前端。
虽然时间是节约了一些,但是后端的代码却暴露在了前端,维护性方面下降了不少,于是想到了BFF。
首先查看了许多已经在运行的成功案例,有些是基于GraphQL重新封装的系统,有些是定制化的系统。经过三天的仔细权衡对比后,决定自己定制化。
主要考虑了两方面,一方面是改造成本,如果基于GraphQL的一些封装库(例如Type-GraphQL、apollo、Prisma等)来设计系统的话,那势必需要了解这些的库的方方面面,并且还需要将其集成到已经的结构中。
另一方面是使用成本,系统完成后是给人用的,不能太复杂,为了避免让使用人员来适应这套系统,最方便的就是将之前的开发流程修改成配置化。
BFF的实现逻辑由后端定义,并且⽆需重构,也不必后端配合改造与联调。
这套系统完成后,会真真切切地影响之前的开发流程,例如不必单独写接口文档,并且可以随时在系统中调试,而不必借助postman调试。
一、前端界面
1)配置
当前80%的Node接口代码复杂度都并不⾼,基本都是机械化重复的,这些接口可分为三部分:参数处理(1)、服务调用(2)和响应聚合(3),类似于下图。

那么前端界面就可以围绕这三部分展开,如下图所示,其中处理器就是服务调用,只是会基于通用接口服务和指定的Model的封装函数。

权限ID就是一段字符串,会在权限系统做接口校验,具体会在后文讲解。模块其实就是之前Router目录中的各个文件,现在将它们作为选项存在。
参数是可以动态配置的,处理器也是一样,并且在选中方法后会显示方法名和方法参数,而在选好Model文件后,会出现查看按钮。

点击查看按钮就能看到Model文件中映射的属性,以及数据库表的字段了,在之前的开发中经常需要查找这些属性和字段,甚是繁琐。
逻辑结构就是接口的主体,除了参数部分的代码不需要写之外,其余代码都在这里完成,是整个接口最为核心的部分。

这部分的处理我其实考虑了很久,在简便和自定义之间找到了一个平衡点,最终才实现了上述效果。
之前声明的参数和处理器都可以在这个编辑器中引用,这个代码编辑器采用的是monaco-editor,微软出品的VS Code浏览器版本,该有的功能都有。
但是我只集成了代码高亮的功能,自动索引的功能没有成功集成进来,顺便说下,官方的API文档非常不友好。
2)调试
在配置化界面的最下方,就是调试部分,当接口创建完成后,就能马上调试。

点击API文本框中的搜索icon,就能看到最终的源码了,能帮助自己迅速定位问题。

3)列表
在列表界面中,包含新建的入口,以及查看和编辑。当跳转到创建页面后,点击浏览器的返回键,列表页面能恢复成之前的样子。

列表页面的状态不会受跳转的影响。点击查看会出现配置信息、源码和调试界面,这些配置信息就是接口文档,并且还能随时调试。

二、后端服务
这次我将API接口的数据都存储在MongoDB中,主要考虑的是数据中会包含大量的数组和JSON对象,若存在MySQL中,就需要在存入和取出时做序列化和反序列化。
1)vm
后端在接收到界面中的参数后,就会将相关参数解析成对应代码,再拼接成一整段的字符串代码。执行这段代码使用的是Node内置的vm模块。
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躬行记(12)——BFF的更多相关文章
- Node.js躬行记(23)——Worker threads
Node.js 官方提供了 Cluster 和 Child process 创建子进程,通过 Worker threads 模块创建子线程.但前者无法共享内存,通信必须使用 JSON 格式,有一定的局 ...
- Node.js躬行记(1)——Buffer、流和EventEmitter
一.Buffer Buffer是一种Node的内置类型,不需要通过require()函数额外引入.它能读取和写入二进制数据,常用于解析网络数据流.文件等. 1)创建 通过new关键字初始化Buffer ...
- Node.js躬行记(2)——文件系统和网络
一.文件系统 fs模块可与文件系统进行交互,封装了常规的POSIX函数.POSIX(Portable Operating System Interface,可移植操作系统接口)是UNIX系统的一个设计 ...
- Node.js躬行记(4)——自建前端监控系统
这套前端监控系统用到的技术栈是:React+MongoDB+Node.js+Koa2.将性能和错误量化.因为自己平时喜欢吃菠萝,所以就取名叫菠萝系统.其实在很早以前就有这个想法,当时已经实现了前端的参 ...
- Node.js躬行记(6)——自制短链系统
短链顾名思义是一种很短的地址,应用广泛,例如页面中有一张二维码图片,包含的是一个原始地址(如下所示),如果二维码中的链接需要修改,那么就得发代码替换掉. 原始地址:https://github.com ...
- Node.js躬行记(15)——活动规则引擎
在日常的业务开发中,会包含许多的业务规则,一般就是用if-else硬编码的方式实现,这样就会增加逻辑的维护成本,若无注释,可能都无法理解规则意图. 因为一旦规则有所改变,那么就需要修改代码再发布代码, ...
- Node.js躬行记(19)——KOA源码分析(上)
本次分析的KOA版本是2.13.1,它非常轻量,诸如路由.模板等功能默认都不提供,需要自己引入相关的中间件. 源码的目录结构比较简单,主要分为3部分,__tests__,lib和docs,从名称中就可 ...
- Node.js躬行记(21)——花10分钟入门Node.js
Node.js 不是一门语言,而是一个基于 V8 引擎的运行时环境,下图是一张架构图. 由图可知,Node.js 底层除了 JavaScript 代码之外,还有大量的 C/C++ 代码. 常说 Nod ...
- Node.js躬行记(3)——命令行工具
一.自定义 创建一个空目录,然后通过npm init命令初始化package.json文件,并按提示输入相关信息或直接回车使用默认信息,生成的内容如下所示. { "name": & ...
随机推荐
- 自旋锁&信号量
1. 自旋锁 Linux内核中最常见的锁是自旋锁.一个自旋锁就是一个互斥设备,它只能有两个值:"锁定"和"解锁".如果锁可用,则"锁定"位被 ...
- BeanUtils中的自动类型转换(二)
javabean package entity; import java.util.Date; /** * 一个测试用: * student,javaBean * @author mzy * 一个标准 ...
- 理解Flink中的Task和SUBTASK
1.概念 Task(任务):Task是一个阶段多个功能相同的subTask 的集合,类似于Spark中的TaskSet. subTask(子任务):subTask是Flink中任务最小执行单元,是一个 ...
- 地图控件:overview、scale、toolbar
地图常用控件: 1.AMap.MapType:地图类型切换插件,用来切换固定的几个常用图层 2.AMap.OverView:地图鹰眼插件,默认在地图右下角显示缩略图 3.AMap.Scale:地图比例 ...
- MySQL中的seconds_behind_master的理解
通过show slave status查看到的Seconds_Behind_Master,从字面上来看,他是slave落后master的秒数,一般情况下,也确实这样,我们可以通过Seconds_Beh ...
- Redis-技术专区-帮从底层彻底吃透AOF技术原理
AOF持久化方式 AOF持久化方式是将redis的操作日志以追加的方式写入磁盘文件中.AOF持久化是以日志的形式记录服务器所处理的每一个写.删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看 ...
- vue 引用 tcplayer 做直播( 俩个例子,都可以用。替换直播地址即可,后端推流,前端观看。 )
例子一比例子二更加容易被理解.另外 m3u8 也支持 webrtc 开头的直播地址. 补充JS 得下载到本地,自行引入: https://imgcache.qq.com/open/qcloud/liv ...
- 矩阵BFS
leetcode 1091矩阵BFS 在一个 N × N 的方形网格中,每个单元格有两种状态:空(0)或者阻塞(1). 一条从左上角到右下角.长度为 k 的畅通路径,由满足下述条件的单元格 C_1, ...
- 离线安装Windows Terminal
Windows Terminal颜值高.适配好.速度快,是Windows 10下命令行工具的不二选择. 最近在公司电脑上安装Windows Terminal时遇到一个问题,由于公司电脑不能直接连接外网 ...
- Spring事务管理回滚问题
Spring事务管理不能回滚问题 在前段时间学习SpringMVC的练习中,碰到声明式事务管理时,事务不能回滚的情况,通过查看博客和资料,解决了问题. 原因 导致Spring事务管理不能回滚的原因有两 ...