Nodejs 使用 NestJS 完成项目后的体验分享,JS 模块添加依赖注入必要性探讨

发布于 1周前 作者 sinazl 来自 nodejs/Nestjs

Nodejs 使用 NestJS 完成项目后的体验分享,JS 模块添加依赖注入必要性探讨
JAVA 里用依赖注入因为定义的都是 class ,想要共享实例只能注入。但 JS 模块导入即单例与 JAVA 机制完全不同,个人感觉必要性不强。
比如在文件开头的 import 语句,其实可以已经体现了依赖关系,这时你完全可以把 service/controller/dao 实例或方法 import 进来,其实已经可以算作完成了依赖声明,为什么还要 module 中重新注入一次依赖关系?
其次在构造函数中把 service/dao 实例变成参数传进来,个人感觉复杂了不少,可维护性降低。

大家觉得用 typescript + express 再分层会不会更简洁一些?


21 回复

JS 没有自省,没办法。
Nest.js 在 Node 工程化上的探索还是不错的。

Nestjs仿小米商城企业级Nodejs RBAC 微服务项目实战视频教程+Docker Swarm K8s云原生分布式部署,这个很不错,https://www.itying.com/goods-1139.html


module 的话这个你需要看 angular 的设计,https://angular.io/guide/ngmodules

我个人理解,模块可以拿来做单例,但不是好的实践。IOC 可以做的事情更多


嗯,一处依赖要在 module/import/构造函数弄三次声明,跟以前写 java 的感觉一模一样。

应该说是 DI


构架看上去不高级,体现不出水平倒是真的

嗯,是的。Nestjs 基本理念来自 Angular 和 Spring ,这些问题两者都有。目前就是在工程化上的妥协,不引入框架,项目代码真的比较难以维护,就怕放飞自我。

虽然说在 js 上搞依赖注入可能看上去有点多此一举,但做成接近 spring 的方案可以减少学习成本啊


有点道理,NG,Spring 过来一看,太亲切了,太熟悉了

个人理解,单例应该是基于 app 应用生命周期的,而不是基于模块的。在 app 只会有一个实例的情况下,使用 DI 看起来会显得多余,但我认为这是明确代码职责的一种方式

为什么要在 module 里定义依赖关系,因为依赖的是接口而不是实现,只不过一般图省事直接把实现类作为接口传进去了,就像 Spring 里面依赖项直接写了某个类而不是接口一样
可以看下这章 https://docs.nestjs.com/fundamentals/custom-providers ,providers: [CatsService] 只是 providers: [
{
provide: CatsService,
useClass: CatsService,
},
] 的简写,provide 属性是 IoC 容器用于寻找实例的 key ,不一定是一个具体的类,碰到需要多个实现的场景,一般定义个抽象类作为 provide ,useClass 就可以根据情况注入不同的实现类了

本质是要把依赖交给容器管理,让使用方不知道用的是哪个实现,如果直接 import 进来用就绑死了具体的实现了

强约束性,比其他框架 service 满天飞好多了

就像 #12 说的一样,大多数人是跳过了写接口这个阶段,直接依赖的实现类。所以看着的结果就是同一个东西在 module 和 import 里重复导入。
providers: [CatsService] 也可以写成
providers: [{provide: AnimalsService, useClass: CatsService}]
这里的 AnimalsService 就可以是个接口,而 useClass 又可以改成 useValue ,useFactory ,useExisting…等等灵活的实现。
这种做法可能在很多 orm 测试用例里会见到,因为测试往往用的不是真的数据,如果要 mock 的话,这时就可以使用 MockService 作为实现。

但其实 99.99%的业务代码用到倒闭也不会有替换 Service 的需求,也就测试时这个不绑死实现的特性有点用,但在 node 里也提供了能力去对 require 的结果进行 hook ,像 jest 不需要依赖注入一样可以替换 requre 、import 的东西



我们使用的就是接口,这玩意更不实用,只有一个类,都用接口代替,平白多了 一层。而且 DEBUG 时很麻烦,点击方法都跑到了接口文件,每次还要手动去搜这个类。

TS 原写模块一般都会有一个 index 文件,只需要改用接口类型声明,如果真有类替换,在 index 中替换掉这个 modlue 的 export 实例即可,不更简单?

强约束性, 前端仔表示, 看了一周文档, 看不下去, 跑了.

自己写个破接口 mock 一下, express 真香.

#16
1.实际情况是 99.99%的项目都不需要接口,所以你直接使用类也完全 ok ,框架也很灵活没有限制死,甚至 provider 就是默认你不写接口模式了。说到 debug 跳转,至少我用的 webstorm 是可以跳转到接口实现类的,没有要搜索的情况。

2.index.ts 的确是一个好东西,在 ng 里叫做 barrel files 。但是使用不当可能会造成循环应用。https://docs.nestjs.com/fundamentals/circular-dependency#circular-dependency

3.如果你想的是通过修改 index.ts 的 export ,来修改对应导出,那就是完全硬编码的方式了。还不如一步到位把所有你可能用到的 provider 全部 export 来得更方便?

4.技术没有银弹,本质上 nest.js 这套还是学的 ng 和 spring ,关于过度设计接口抽象的话题网上比比皆是。我的看法是,结合自身和团队情况,程序不报错,那条条大路通罗马。

nestjs 最大的好处就是可以不招后端,只招前端就可以了,人力成本省了一大笔,这对于创业公司或者新项目是巨大的收益



NestJS 的 Controller 其实设计的不错,可以省掉每个 url 的前辍像 /api/v2 这种,还能省掉 app.get/app.post 重复定义。如果只用 express ,controller 代码看上去有点脏

用 nextjs 、或者 java 的创业公司基本都死光了, 有 使用 Spring 的耐心和实力,直接上 java 不香吗?

框架搞成这样,其实跟 Nodejs 没有多大关系了,跟 JS 更没啥关系了。

在Node.js中使用NestJS框架完成项目后,我深刻体会到了其强大的架构能力和开发效率的提升。NestJS通过引入了许多现代后端开发的概念,比如依赖注入(DI),使得项目结构更加清晰,代码更加模块化。

关于JS模块添加依赖注入的必要性,我认为主要体现在以下几个方面:

  1. 提高代码可维护性:依赖注入使得模块之间的依赖关系更加明确,通过配置文件或注解来管理依赖,可以很方便地追踪和管理依赖关系,提高代码的可维护性。

  2. 增强代码可读性:通过依赖注入,我们可以避免在模块内部直接创建依赖对象的实例,而是通过构造函数注入或其他方式获取依赖,这样可以使代码更加简洁,提高可读性。

  3. 便于单元测试:依赖注入使得模块之间的依赖可以很方便地进行替换,这在单元测试中非常有用。我们可以使用Mock对象来替换实际的依赖对象,从而方便地进行单元测试。

以下是一个简单的NestJS依赖注入示例:

import { Controller, Get, Injectable } from '@nestjs/common';

@Injectable()
class UserService {
  getUser(): string {
    return 'User';
  }
}

@Controller()
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  getUser(): string {
    return this.userService.getUser();
  }
}

在这个示例中,UserService通过@Injectable()注解被标记为一个可以注入的服务,UserController通过构造函数注入了UserService,从而实现了依赖注入。这种方式使得代码更加模块化和易于测试。

回到顶部