NestJs学习之旅(4)——模块系统
2019年8月22日 · 270 字 · 2 分钟
本文是NestJs学习之旅的第四篇,讲解模块系统。
模块
NestJs中模块是构建和组织业务单元的基本元素。使用@Module()
装饰模块来声明该模块的元信息:
- 本模块导出哪些服务提供者
- 本模块导入了哪些依赖模块
- 本模块提供了哪些控制器
每个NestJs至少有一个跟模块,这个就是app.module.ts
定义的。根模块一般不放具体的业务逻辑,具体业务逻辑应该下沉到各个子业务模块去做。
比如我们开发一个商城系统,该系统有以下业务模块:
- 订单中心
- 用户中心
- 支付中心
- 商品中心
- 物流中心
那我们可以定义以下的模块结构:
|-- app.module.ts
|-- order
|-- order.module.ts
|-- services
|-- order.service.ts
|-- controllers
|-- order.controller.ts
|-- user
|-- user.module.ts
|-- services
|-- user.service.ts
|-- controllers
|-- user.controller.ts
|-- pay
|-- pay.module.ts
|-- services
|-- wepay.service.ts
|-- alipay.service.ts
|-- pay.service.ts
|-- controller
|-- pay.controller.ts
...
模块化有以下优点:
- 业务低耦合
- 边界清晰
- 便于排查错误
- 便于维护
模块声明与配置
@Module()
装饰的类为模块类
,该装饰器的典型用法如下:
@Module({
providers: [UserService],
controllers: [UserController],
imports: [OrderModule],
exports: [UserService]
})
export class UserModule {
}
参数名称 | 说明 |
---|---|
proviers | 服务提供者列表,本模块可用,可以自动注入 |
controllers | 控制器列表,本模块可用,用来绑定路由访问 |
imports | 本模块导入的模块,如果需要使用到其他模块的服务提供者,此处必须导入其他模块 |
exports | 本模块导出的服务提供者,只有在此处定义的服务提供者才能在其他模块使用 |
模块重导出
ts中有以下用法:
// a.ts
export interface A {
}
// index.ts
export * from './a';
我们在使用的时候直接使用以下代码即可,方面封装
import {A} from './index'
NestJs中的模块也有类似用法,比如我们定义了两个基本模块,这两个基本模块用的时候基本都是一起导入的,此时我们通过模块重导出将其封装到一个叫CoreModule
,其他地方直接导入CoreModule
即可。
@Module({
providers: [CommonService],
exports: [CommonService]
})
export class CommonModule {}
@Module({
providers: [Util],
exports: [Util]
})
export class UtilModule {}
@Module({
imports: [CommonModule, UtilModule],
exports: [CommonModule, UtilModule]
})
export class CoreModule {}
模块初始化与依赖注入
如果需要在模块实例化的时候运行一些逻辑,而且该逻辑有外部依赖的时候,可以通过以下方式处理
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class catsModule {
constructor(private readonly userService: UserService) { // 没有@Inject
// 调用userService
}
}
全局模块
上面定义的模块都是需要手动imports
进来的,如果有些模块是使用率很高的,比如工具模块,此时可以声明为全局模块。
使用@Global()
即可声明全局模块。
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Global()
@Module({
controllers: [UserController],
providers: [UserService],
})
export class catsModule {
}
动态模块
上面定义的都是静态模块,如果我们需要动态声明我们的模块,比如数据库模块,连接成功我才返回模块,此时需要使用动态模块来处理。
使用模块名.forRoot()
方法来返回模块定义,通过该方式定义的即为动态模块。
@Module({
providers: [DatabaseProvider]
})
export class DatabaseModule {
static async forRoot(env: string) {
const provider = createDatabaseProvider(env); // 根据环境变量连接不同的数据库
return {
module: DatabaseModule,
providers: [provider],
exports: [provider]
}
}
}
// user.module.ts
@Module({
imports: [DatabaseModule.forRoot('production')]
})
export class UserModule {}
生产环境下的姿势
上面有一个商城系统的模块例子,当我们的业务模块开发完毕之后,需要将其注册到AppModule,这样才能生效,这个也有个好处,有点像插拔的例子,当需要下掉一个业务时,业务代码不动,在AppModule取消注册即可。
@Module({
imports:[UserModule,GoodsModule,OrderModule,PayModule]
})
export class AppModule {}
结尾
模块系统是NestJs另一个重要的特性,个人认为是基于DDD思想的,每个模块就是一个单独的领域业务,可以由一个小组去独立开发。多个模块时可以同时开发,如果有依赖问题的话,可以先把模块和响应的interface公开出去,别人正常调用你的interface,当实现类开发完毕之后NestJs会自动注入该实现类,调用方的代码不用更改。