news 2026/4/18 20:36:41

nestjs 架构篇:用模块来组织代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
nestjs 架构篇:用模块来组织代码

一、什么是模块?

在 NestJS 中,模块是用@Module()装饰器注释的类。每个 NestJS 应用至少有一个根模块(通常是AppModule)。模块提供了编译时的上下文,用于组织控制器、提供者(服务、仓库等)以及其他相关模块。

模块的主要作用:

  • 将相关的功能聚合在一起(如用户模块、订单模块)。

  • 声明模块内部的组件(控制器、提供者)哪些可以对外暴露,哪些是私有的。

  • 管理模块之间的依赖关系(导入其他模块的功能)。

  • 支持动态模块,实现可配置的模块。

二、@Module()装饰器配置项

@Module()接受一个对象,包含以下常用属性:

属性作用
providers由 Nest 注入器实例化的提供者(服务、仓库、工厂等),至少在当前模块中可用。
controllers必须创建的控制器集合(处理 HTTP 请求)。
imports导入其他模块中导出的提供者,以便在当前模块中使用。
exports导出当前模块中的提供者,供其他导入本模块的模块使用。
import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; @Module({ controllers: [UsersController], // 注册控制器 providers: [UsersService], // 注册服务 exports: [UsersService], // 导出 UsersService 供其他模块使用 }) export class UsersModule {}

三、模块的基本用法

1. 功能模块化

按业务功能划分模块,例如用户模块、产品模块、订单模块。

src/ ├── users/ │ ├── users.module.ts │ ├── users.controller.ts │ ├── users.service.ts │ └── entities/ ├── products/ │ ├── products.module.ts │ ├── products.controller.ts │ └── products.service.ts └── app.module.ts

users.module.ts

@Module({ controllers: [UsersController], providers: [UsersService], exports: [UsersService], // 如果其他模块需要 UserService }) export class UsersModule {}

app.module.ts导入这些功能模块:

@Module({ imports: [UsersModule, ProductsModule], }) export class AppModule {}

2. 共享模块

NestJS 模块默认是单例的,并且提供者默认是共享的。如果你想让一个模块的提供者被其他模块使用,只需要在exports数组中列出它们。

// common.module.ts @Module({ providers: [CommonService, HelperService], exports: [CommonService], // 只导出 CommonService,HelperService 对外不可见 }) export class CommonModule {}

其他模块导入CommonModule后,就可以注入CommonService,但不能注入HelperService(因为它未被导出)。

3. 全局模块

如果希望一个模块到处可用,无需在每个使用模块中导入,可以使用@Global()装饰器。全局模块通常用于基础设施(如数据库连接、日志服务)。

import { Global, Module } from '@nestjs/common'; @Global() @Module({ providers: [DatabaseService], exports: [DatabaseService], }) export class DatabaseModule {}

现在任何模块(无需导入DatabaseModule)都可以直接注入DatabaseService

注意:谨慎使用全局模块,避免命名冲突和不清晰的依赖关系。

四、模块的依赖注入

模块本身也可以注入提供者(但模块类通常不需要被注入)。模块类可以有自己的构造函数,并接收通过providers注册的服务,但更常见的做法是将提供者注入到控制器或服务中。

@Module({ providers: [ConfigService], }) export class AppModule { constructor(private configService: ConfigService) { // 可以在模块类中使用注入的服务,但实际业务中很少这样写 } }

五、模块封装与作用域

  • 提供者作用域:默认是模块作用域,即提供者在所属模块内可见。只有被exports导出的提供者才能被其他模块使用。

  • 控制器作用域:控制器必须在所属模块的controllers数组中声明,不能跨模块直接使用(除非通过路由导入其他模块的控制器,但通常不会这样做)。

  • 模块间通信:通过导入导出提供者来实现功能共享,而不是直接引用其他模块的控制器。

六、动态模块

动态模块允许你在导入时传递配置参数,从而创建可定制的模块。动态模块可以返回一个动态的Module定义(可以修改providersexports等)。

示例:可配置的日志模块

// logger.module.ts import { Module, DynamicModule } from '@nestjs/common'; import { LoggerService } from './logger.service'; @Module({}) export class LoggerModule { static forRoot(options: { level: string }): DynamicModule { return { module: LoggerModule, providers: [ { provide: 'LOG_LEVEL', useValue: options.level, }, LoggerService, ], exports: [LoggerService], }; } }

使用动态模块:

@Module({ imports: [LoggerModule.forRoot({ level: 'debug' })], }) export class AppModule {}

NestJS 提供了几种约定方法名:register(),forRoot(),forFeature()等,用于不同场景(如配置模块、异步模块)。

七、模块重导出

你可以通过exports数组直接重导出其他模块,这样导入当前模块的模块就能间接获得被重导出模块的提供者。

@Module({ imports: [CommonModule], exports: [CommonModule], // 重导出 CommonModule }) export class CoreModule {}

这样,任何导入CoreModule的模块都会自动获得CommonModule导出的提供者。

八、最佳实践与常见模式

1. 按功能域划分模块

每个功能模块应包含自己的控制器、服务、实体、DTO 等,保持高内聚低耦合。

2. 使用共享模块集中管理公共组件

创建一个SharedModule,导入并导出公共服务(如HelperService,DateService),然后让其他模块导入SharedModule

3. 避免循环依赖

如果模块 A 和模块 B 互相导入,会导致循环依赖。解决方法:

  • 将共享提供者提取到独立模块中。

  • 使用forwardRef(() => Module)延迟引用(不推荐,应优先重构)。

4. 使用动态模块进行可配置集成

对于数据库、缓存、消息队列等需要配置的集成,提供动态模块方法(如forRoot(),register()

九、完整示例:用户模块 + 数据库模块

database.module.ts(全局动态模块)

import { Global, Module, DynamicModule } from '@nestjs/common'; import { createConnection } from 'typeorm'; @Global() @Module({}) export class DatabaseModule { static forRoot(entities: Function[]): DynamicModule { const connectionProvider = { provide: 'CONNECTION', useFactory: async () => await createConnection({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities, }), }; return { module: DatabaseModule, providers: [connectionProvider], exports: [connectionProvider], }; } }

users.module.ts

import { Module } from '@nestjs/common'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import { User } from './user.entity'; @Module({ imports: [DatabaseModule.forRoot([User])], // 传入实体 controllers: [UsersController], providers: [UsersService], }) export class UsersModule {}

app.module.ts

@Module({ imports: [UsersModule], }) export class AppModule {}

十、总结

  • 模块是 NestJS 架构的组织单元,通过@Module()声明。

  • 每个模块可以包含控制器提供者,并可以导入其他模块、导出自己的提供者。

  • 模块默认是单例作用域,提供者默认是模块内共享的。

  • 使用@Global()创建全局模块,减少导入次数。

  • 动态模块允许在导入时配置模块,实现高度可定制。

  • 良好的模块设计应当高内聚、低耦合,按业务功能划分。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 20:31:13

为什么DeepMind放弃通用智能路径,而华为盘古、通义千问坚持AGI架构?——基于17家机构2023–2024技术路线图的逆向推演(含未公开专利链分析)

第一章:AGI研发的国际竞争格局 2026奇点智能技术大会(https://ml-summit.org) 全球通用人工智能(AGI)研发已进入国家战略竞速阶段,美、中、欧、日、韩等主要经济体正通过顶层政策设计、大规模算力基建投入与前沿基础模型研究形成…

作者头像 李华
网站建设 2026/4/18 20:31:12

GDSDecomp深度解析:Godot游戏逆向工程的架构设计与性能优化

GDSDecomp深度解析:Godot游戏逆向工程的架构设计与性能优化 【免费下载链接】gdsdecomp Godot reverse engineering tools 项目地址: https://gitcode.com/GitHub_Trending/gd/gdsdecomp GDSDecomp是Godot游戏引擎的逆向工程工具套件,专注于PCK资…

作者头像 李华
网站建设 2026/4/18 20:30:20

【知识图谱】从构建到应用:技术全景与实践指南

1. 知识图谱:从概念到价值 第一次接触知识图谱时,我正为一个电商项目头疼——用户搜索"适合送女友的生日礼物",系统只会机械地返回"生日蛋糕""鲜花"等关键词匹配结果。直到引入知识图谱技术后,系统…

作者头像 李华
网站建设 2026/4/18 20:30:01

Mimics.19生成的STL文件有杂点?教你三招清理离散数据,让模型更干净

Mimics.19生成的STL文件有杂点?三招专业级离散数据清理术 当你从Mimics.19导出STL文件时,是否经常遇到模型表面出现"雪花状"杂点或游离的离散点云?这些看似微小的数据噪声,实际上会严重影响后续的3D打印成品质量、有限元…

作者头像 李华
网站建设 2026/4/18 20:29:07

思科紧急修复高危 ISE 漏洞

聚焦源代码安全,网罗国内外最新资讯!编译:代码卫士思科发布紧急安全公告,提醒用户称其 ISE 和 ISE-IPC 产品中存在多个漏洞,可导致经过身份认证的远程攻击者在受影响设备上执行任意命令。这些漏洞还可能导致路径遍历攻…

作者头像 李华