NestJS 模块化设计
后端架构658 字预计 2 分钟阅读
学习 Node.js 企业级框架 NestJS 的模块化架构设计思想,探讨依赖注入与控制反转的落地实践。
引言
在 Node.js 生态中,Express 和 Koa 提供了极简的开发体验,但在大型复杂项目上却容易由于缺乏规范导致代码难以维护。NestJS 引入了 Angular 的设计哲学,通过控制反转(IoC)和依赖注入(DI),为后端提供了一套高度可扩展、松耦合的模块化架构。
模块化系统与 Module 装饰器
Module 的定义
在 NestJS 中,应用是由一个个模块(Module)构成的网格。每个模块都是一个使用 @Module() 装饰器修饰的类。它接收一个元数据对象,用来描述模块的结构:
- providers:由 Nest 注入器实例化并可在该模块内共享的提供者(如 Service)。
- controllers:该模块定义的控制器,负责处理传入的 HTTP 请求。
- imports:当前模块运行所依赖的其他模块列表。
- exports:当前模块要向外导出的提供者,使其能被其他导入本模块的模块使用。
模块的边界与封装
默认情况下,Nest 中的模块是高度封装的。在一个模块中声明的 Service,除非显式放到 exports 中,否则在其他模块中是无法被注入使用的。这有助于保持清晰的代码边界,防止模块间产生复杂的网状依赖。
依赖注入 (DI) 的高级用法
构造函数注入
这是 Nest 最常用的注入方式。Nest 运行时会根据 TypeScript 的类型注解自动找到对应的 Provider 实例并注入:
@Injectable()
export class UsersService {
constructor(private readonly usersRepository: UsersRepository) {}
}
动态模块 (Dynamic Modules)
有些模块需要根据不同的传入参数来改变其行为,例如数据库连接模块、配置模块。通过动态模块机制,我们可以让模块暴露出自定义的静态方法(如 forRoot),允许用户在导入时传递动态参数:
@Module({})
export class ConfigModule {
static forRoot(options: ConfigOptions): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options,
},
ConfigService,
],
exports: [ConfigService],
};
}
}
异常过滤器与全局管道
利用 Pipe 进行参数校验
Nest 提供了内置的 ValidationPipe。结合 class-validator 库,我们可以仅通过在 DTO 类中编写装饰器,来实现全自动的输入参数校验,而无需在业务代码中编写任何冗余的判断逻辑。
利用 Exception Filter 统一错误返回
为了防止底层异常(如数据库连接失败)以明文形式直接暴露给前端,我们可以定制全局异常过滤器,捕获所有抛出的错误,将其包装为规范的 JSON 格式后统一返回。