百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

从Nest到Nesk -- 模块化Node框架的实践

ccwgpt 2024-09-29 10:01 35 浏览 0 评论

文: 达孚(沪江Web前端架构师)

本文原创,转至沪江技术

首先上一下项目地址(:>):

Nest:https://github.com/nestjs/nest

Nesk:https://github.com/kyoko-df/nesk

Nest初认识

Nest是一个深受angular激发的基于express的node框架,按照官网说明是一个旨在提供一个开箱即用的应用程序体系结构,允许轻松创建高度可测试,可扩展,松散耦合且易于维护的应用程序。

在设计层面虽然说是深受angular激发,但其实从后端开发角度来说类似于大家熟悉的Java Spring架构,使用了大量切面编程技巧,再通过装饰器的结合完全了关注上的分离。同时使用了Typescript(也支持Javascript)为主要开发语言,更保证了整个后端系统的健壮性。

强大的Nest架构

那首先为什么需要Nest框架,我们从去年开始大规模使用Node来替代原有的后端View层开发,给予了前端开发除了SPA以外的前后端分离方式。早期Node层的工作很简单-渲染页面代理接口,但在渐渐使用中大家会给Node层更多的寄托,尤其是一些内部项目中,你让后端还要将一些现有的SOA接口进行包装,对方往往是不愿意的。那么我们势必要在Node层承接更多的业务,包括不限于对数据的组合包装,对请求的权限校验,对请求数据的validate等等,早期我们的框架是最传统的MVC架构,但是我们翻阅业务代码,往往最后变成复杂且很难维护的Controller层代码(从权限校验到页面渲染一把撸到底:))。

那么我们现在看看Nest可以做什么?从一个最简单的官方例子开始看:

async function bootstrap() { const app = await NestFactory.create(ApplicationModule);
 app.useGlobalPipes(new ValidationPipe()); await app.listen(3000);
}
bootstrap();

这里就启动了一个nest实例,先不看这个ValidationPipe,看ApplicationModule的内容:

@Module({
 imports: [CatsModule],
})export class ApplicationModule implements NestModule {
 configure(consumer: MiddlewaresConsumer): void {
 consumer
 .apply(LoggerMiddleware)
 .with('ApplicationModule')
 .forRoutes(CatsController);
 }
}
@Module({
 controllers: [CatsController],
 components: [CatsService],
})export class CatsModule {}

这里看到nest的第一层入口module,也就是模块化开发的根本,所有的controller,component等等都可以根据业务切分到某个模块,然后模块之间还可以嵌套,成为一个完整的体系,借用张nest官方的图:

在nest中的component概念其实一切可以注入的对象,对于依赖注入这个概念在此不做深入解释,可以理解为开发者不需要实例化类,框架会进行实例化且保存为单例供使用。

@Controller('cats')@UseGuards(RolesGuard)@UseInterceptors(LoggingInterceptor, TransformInterceptor)export class CatsController { constructor(private readonly catsService: CatsService) {} @Post() @Roles('admin') async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto);
 } @Get() async findAll(): Promise<Cat[]> { return this.catsService.findAll();
 } @Get(':id')
 findOne( @Param('id', new ParseIntPipe())
 id,
 ): Promise<Cat> { return this.catsService.findOne(id);
 }
}

Controller的代码非常精简,很多重复的工作都通过guards和interceptors解决,第一个装饰器Controller可以接受一个字符串参数,即为路由参数,也就是这个Controller会负责/cats路由下的所有处理。首先RolesGuard会进行权限校验,这个校验是自己实现的,大致结构如下:

@Guard()export class RolesGuard implements CanActivate { constructor(private readonly reflector: Reflector) {}
 canActivate(request, context: ExecutionContext): boolean { const { parent, handler } = context; const roles = this.reflector.get<string[]>('roles', handler); if (!roles) { return true;
 } // 自行实现
 }
}

context可以获取controller的相关信息,再通过反射拿到handler上是否有定义roles的元信息,如果有就可以在逻辑里根据自己实现的auth方法或者用户类型来决定是否让用户访问相关handler。

interceptors即拦截器,它可以:

  • 在方法执行之前/之后绑定额外的逻辑

  • 转换从函数返回的结果

  • 转换从函数抛出的异常

  • 根据所选条件完全重写函数 (例如, 缓存目的)

本示例有两个拦截器一个用来记录函数执行的时间,另一个对结果进行一层包装,这两个需求都是开发中很常见的需求,而且拦截器会提供一个rxjs的观察者流来处理函数返回,支持异步函数,我们可以通过map()来mutate这个流的结果,可以通过do运算符来观察函数观察序列的执行状况,另外可以通过不返回流的方式,从而阻止函数的执行,LoggingInterceptor例子如下:

@Interceptor()export class LoggingInterceptor implements NestInterceptor {
 intercept(dataOrRequest, context: ExecutionContext, stream$: Observable<any>): Observable<any> { console.log('Before...'); const now = Date.now(); return stream$.do( () => console.log(`After... ${Date.now() - now}ms`),
 );
 }
}

回到最初的ValidationPipe,它是一个强大的校验工具,我们看到前面的controller代码中插入操作中有一个CreateCatDto,dto是一种数据传输对象,一个dto可以这样定义:

export class CreateCatDto { @IsString() readonly name: string; @IsInt() readonly age: number; @IsString() readonly breed: string;
}

然后ValidationPipe会检查body是否符合这个dto,如果不符合就会就会执行你在pipe中设置的处理方案。具体是如何实现的可以再写一篇文章了,所以我推荐你看nest中文指南(顺便感谢翻译的同学们)

示例的完整代码可以看01-cats-app

也就是说业务团队中的熟练工或者架构师可以开发大量的模块,中间件,异常过滤器,管道,看守器,拦截器等等,而不太熟练的开发者只需要完成controller的开发,在controller上像搭积木般使用这些设施,即完成了对业务的完整搭建。

Nesk-一个落地方案的尝试

虽然我个人很喜欢Nest,但是我们公司已经有一套基于koa2的成熟框架Aconite,而Nest是基于express的,查看了下Nest的源码,对express有一定的依赖,但是koa2和express在都支持async语法后,差异属于可控范围下。另外nest接受一个express的实例,在nesk中我们只需要调整为koa实例,那么也可以是继承于koa的任何项目实例,我们的框架在2.0版本也是一个在koa上继承下来的node框架,基于此,我们只需要一个简单的adapter层就可以无缝接入Aconite到nesk中,这样减少了nesk和内部服务的捆绑,而将所有的公共内部服务整合保留在Aconite中。Nest对于我们来说只是一个更完美的开发范式,不承接任何公共模块。

所以我们需要的工作可以简单总结为:

  1. 支持Koa

  2. 适配Aconite

支持Koa我们在Nest的基础上做了一些小改动完成了Nesk来兼容Koa体系。我们只需要完成Nesk和Aconite中间的Adapter层,就可以完成Nesk的落地,最后启动处的代码变成:

import { NeskFactory } from '@neskjs/core';import { NeskAconite } from '@hujiang/nesk-aconite';import { ApplicationModule } from './app.module';import { config } from './common/config';import { middwares } from './common/middlware';async function bootstrap() { const server = new NeskAconite({
 projectRoot: __dirname,
 middlewares,
 config
 }); const app = await NeskFactory.create(ApplicationModule, server); await app.listen(config.port);
}

最后Nest有很多@nest scope下的包,方便一些工具接入nest,如果他们与express没有关系,我们其实是可以直接使用的。但是包内部往往依赖@nest/common或者@nesk/core,这里可以使用module-alias,进行一个重指向(你可以尝试下graphql的例子):

"_moduleAliases": { "@nestjs/common": "node_modules/@neskjs/common", "@nestjs/core": "node_modules/@neskjs/core"}

Nesk的地址Nesk,我们对Nesk做了基本流程测试目前覆盖了common和core,其它的在等待改进,欢迎一切愿意一起改动的开发者。

不足与期待

其实从一个更好的方面来说,我们应当允许nest接受不同的底层框架,即既可以使用express,也可以使用koa,通过一个adapter层抹平差异。不过这一块的改造成本会大一些。

另一方面nest有一些本身的不足,在依赖注入上,还是选择了ReflectiveInjector,而Angular已经开始使用了StaticInjector,理论上StaticInjector减少了对Map层级的查找,有更好的性能,这也是我们决定分叉出一个nesk的原因,可以做一些更大胆的内部代码修改。另外angular的依赖注入更强大,有例如useFactory和deps等方便测试替换的功能,是需要nest补充的.

最后所有的基于Koa的框架都会问到一个问题,能不能兼容eggjs(:)),其实无论是Nest还是Nesk都是一个强制开发规范的框架,只要eggjs还建立在koa的基础上,就可以完成集成,只是eggjs在启动层面的改动较大,而且开发范式和nest差异比较多,两者的融合并没有显著的优势。

总之Node作为一个比较灵活的后端开发方式,每个人心中都有自己觉得合适的开发范式,如果你喜欢这种方式,不妨尝试下Nest或者Nesk。

相关推荐

滨州维修服务部“一区一策”强服务

今年以来,胜利油田地面工程维修中心滨州维修服务部探索实施“一区一策”服务模式,持续拓展新技术应用场景,以优质的服务、先进的技术,助力解决管理区各类维修难题。服务部坚持问题导向,常态化对服务范围内的13...

谷歌A2A协议和MCP协议有什么区别?A2A和MCP的差异是什么?

在人工智能的快速发展中,如何实现AI模型与外部系统的高效协作成为关键问题。谷歌主导的A2A协议(Agent-to-AgentProtocol)和Anthropic公司提出的MCP协议(ModelC...

谷歌大脑用架构搜索发现更好的特征金字塔结构,超越Mask-RCNN等

【新智元导读】谷歌大脑的研究人员发表最新成果,他们采用神经结构搜索发现了一种新的特征金字塔结构NAS-FPN,可实现比MaskR-CNN、FPN、SSD更快更好的目标检测。目前用于目标检测的最先...

一文彻底搞懂谷歌的Agent2Agent(A2A)协议

前段时间,相信大家都被谷歌发布的Agent2Agent开源协议刷屏了,简称A2A。谷歌官方也表示,A2A是在MCP之后的补充,也就是MCP可以强化大模型/Agent的能力,但每个大模型/Agent互为...

谷歌提出创新神经记忆架构,突破Transformer长上下文限制

让AI模型拥有人类的记忆能力一直是学界关注的重要课题。传统的深度学习模型虽然在许多任务上取得了显著成效,但在处理需要长期记忆的任务时往往力不从心。就像人类可以轻松记住数天前看过的文章重点,但目前的...

不懂设计?AI助力,人人都能成为UI设计师!

最近公司UI资源十分紧张,急需要通过AI来解决UI人员不足问题,我在网上发现了几款AI应用非常适合用来进行UI设计。以下是一些目前非常流行且功能强大的工具,它们能够提高UI设计效率,并帮助设计师创造出...

速来!手把手教你用AI完成UI界面设计

晨星技术说晨星技术小课堂第二季谭同学-联想晨星用户体验设计师-【晨星小课堂】讲师通过简单、清晰的语言描述就能够用几十秒自动生成一组可编辑的UI界面,AIGC对于UI设计师而言已经逐步发展成了帮助我们...

「分享」一端录制,多端使用的便捷 UI 自动化测试工具,开源

一、项目介绍Recorder是一款UI录制和回归测试工具,用于录制浏览器页面UI的操作。通过UIRecorder的录制功能,可以在自测的同时,完成测试过程的录制,生成JavaScr...

APP自动化测试系列之Appium介绍及运行原理

在面试APP自动化时,有的面试官可能会问Appium的运行原理,以下介绍Appium运行原理。Appium介绍Appium概念Appium是一个开源测试自动化框架,可用于原生,混合和移动Web应用程序...

【推荐】一个基于 SpringBoot 框架开发的 OA 办公自动化系统

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!项目介绍oasys是一个基于springboot框架开发的OA办公自动化系统,旨在提高组织的日常运作和管理...

自动化实践之:从UI到接口,Playwright给你全包了!

作者:京东保险宋阳1背景在车险系统中,对接保司的数量众多。每当系统有新功能迭代后,基本上各个保司的报价流程都需要进行回归测试。由于保司数量多,回归测试的场景也会变得重复而繁琐,给测试团队带来了巨大的...

销帮帮CRM移动端UI自动化测试实践:Playwright的落地与应用

实施背景销帮帮自2015年成立以来,移动端UI自动化测试的落地举步维艰,移动端的UI自动化测试一直以来都未取得良好的落地。然而移动互联网时代,怎样落地移动端的UI自动化测试以快速稳定进行移动端的端到端...

编写自动化框架不知道该如何记录日志吗?3个方法打包呈现给你。

目录结构1.loguru介绍1.1什么是日志?程序运行过程中,难免会遇到各种报错。如果这种报错是在本地发现的,你还可以进行debug。但是如果程序已经上线了,你就不能使用debug方式了...

聊聊Python自动化脚本部署服务器全流程(详细)

来源:AirPython作者:星安果1.前言大家好,我是安果!日常编写的Python自动化程序,如果在本地运行稳定后,就可以考虑将它部署到服务器,结合定时任务完全解放双手但是,由于自动化程序与平...

「干货分享」推荐5个可以让你事半功倍的Python自动化脚本

作者:俊欣来源:关于数据分析与可视化相信大家都听说自动化流水线、自动化办公等专业术语,在尽量少的人工干预的情况下,机器就可以根据固定的程序指令来完成任务,大大提高了工作效率。今天小编来为大家介绍几个P...

取消回复欢迎 发表评论: