Nest.js 模型
模块是一个用 @Module() 装饰器注解的类。 @Module() 装饰器提供了 Nest 用来组织应用程序结构的元数据。
每个应用程序至少有一个模块,一个根模块。 根模块是 Nest 用来构建应用程序图的起点——Nest 用来解决模块和提供者关系和依赖关系的内部数据结构。 虽然理论上非常小的应用程序可能只有根模块,但这不是典型的情况。 我们要强调的是,强烈建议将模块作为组织组件的有效方式。 因此,对于大多数应用程序,最终的架构将采用多个模块,每个模块都封装了一组密切相关的功能。
@module() 装饰器接受一个描述模块属性的对象:
对象 | 描述 |
---|---|
providers | 由 Nest 注入器实例化的提供者,并且可以至少在整个模块中共享 |
controllers | 必须创建的一组控制器 |
imports | 导入模块的列表,这些模块导出了此模块中所需提供者 |
exports | 由本模块提供并应在其他模块中可用的提供者的子集。 |
默认情况下,该模块封装提供程序。这意味着无法注入既不是当前模块的直接组成部分,也不是从导入的模块导出的提供程序。因此,我们可以将从模块导出的提供程序视为模块的公共接口或API。
功能模型
CatsController
和 CatsService
属于同一个应用程序域。 由于它们密切相关,因此将它们移动到功能模型中是有意义的。 功能模型只是组织与特定功能相关的代码,保持代码组织并建立清晰的边界。 这有助于我们管理复杂性并使用 SOLID 原则进行开发,尤其是随着应用程序和/或团队规模的扩大。
为了证明这一点,我们将创建 CatsModule
。
cats/cats.module.ts
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule {}
提示
: 要使用 CLI 创建模型,只需执行下面的命令。$ nest g module cats
上面,我们在 cats.module.ts
文件中定义了CatsModule,并将与该模型相关的所有内容都移到cats 目录中。 我们需要做的最后一件事是将此模型导入根模型(AppModule,在 app.module.ts 文件中定义)。
app.module.ts
import { Module } from '@nestjs/common'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class AppModule {}
这是我们的目录结构现在的样子:
共享模型
在 Nest 中,模型默认是单例的,因此我们可以轻松地在多个模型之间共享任何提供程序的同一个实例。
每个模型都自动成为共享模型。 一旦创建,它就可以被任何模型重用。 假设我们想在其他几个模型之间共享 CatsService 的一个实例。 为此,我们首先需要通过将 CatsService 提供程序添加到模型的导出数组中来导出它,如下所示:
cats.module.ts
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], exports: [CatsService] }) export class CatsModule {}
现在任何导入 CatsModule 的模型都可以访问 CatsService 并且将与导入它的所有其他模型共享同一个实例。
模型二次导出
如上所示,模型可以导出其内部提供者。 此外,他们可以重新导出他们导入的模型。 在下面的示例中,CommonModule 既可以从 CoreModule 导入也可以从 CoreModule 导出,使其可用于导入该模型的其他模型。
@Module({
imports: [CommonModule],
exports: [CommonModule],
})
export class CoreModule {}
依赖注入
模型类也可以注入提供者(例如,出于配置目的):
cats.module.ts
import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule { constructor(private catsService: CatsService) {} }
但是,由于循环依赖,模型类本身不能作为提供者注入。
全局模型
如果我们必须在任何地方导入相同的模型集,这可能会变得乏味。 与 Nest 不同,Angular providers 是在全局范围内注册的。 一旦定义,它们在任何地方都可用。 然而,Nest 将提供程序封装在模块范围内。 如果不首先导入封装模型,我们将无法在其他地方使用模型的提供程序。
当想提供一组开箱即用的提供程序时(例如,帮助程序、数据库连接等),请使用 @Global()
装饰器将模型设为全局。
import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Global()
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
@Global()
装饰器使模型具有全局作用域。 全局模型应该只注册一次,通常由根模型或核心模型注册。 在上面的示例中,CatsService 提供者将无处不在,希望注入服务的模型不需要在其导入数组中导入 CatsModule。
提示
: 让一切都全局化并不是一个好的设计决策。 全局模型可用于减少必要的模板数量。 导入数组通常是使模型的 API 可供消费者使用的首选方式。
动态模型
Nest 模型系统包括一个强大的功能,称为动态模型。 此功能使我们能够轻松创建可动态注册和配置提供程序的可定制模型。 动态模型在这里被广泛介绍。 在本章中,我们将简要概述以完成对模型的介绍。
以下是 DatabaseModule
的动态模型定义示例:
import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';
@Module({
providers: [Connection],
})
export class DatabaseModule {
static forRoot(entities = [], options?): DynamicModule {
const providers = createDatabaseProviders(options, entities);
return {
module: DatabaseModule,
providers: providers,
exports: providers,
};
}
}
提示
:forRoot() 方法可以同步或异步(即通过 Promise)返回动态模型。
此模块 Connection 默认情况下(在 @Module() 装饰器元数据中)定义提供程序,但此外-根据传递给 forRoot()
方法的 entities 和 options 对象 -公开提供程序的集合,例如存储库。请注意,动态模型返回的属性扩展(而不是覆盖)@Module() 装饰器中定义的基本模型元数据。这就是从模型导出静态声明的 Connection 提供程序和动态生成的存储库提供程序的方式。
如果要在全局范围内注册动态模型,请将 global 属性设置为 true。
{
global: true,
module: DatabaseModule,
providers: providers,
exports: providers,
}
警告
: 如上所述,让一切都全局化并不是一个好的设计决策。
DatabaseModule
可以通过以下方式导入和配置:
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';
@Module({
imports: [DatabaseModule.forRoot([User])],
})
export class AppModule {}
如果你想反过来重新导出一个动态模型,你可以省略导出数组中的 forRoot() 方法调用:
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';
@Module({
imports: [DatabaseModule.forRoot([User])],
exports: [DatabaseModule],
})
export class AppModule {}
动态模型一章更详细地介绍了这个主题,并包含一个完整代码示例。