模块
什么是模块?
想象一下,你在组织一个大型公司:
- 公司有不同的部门(模块):人事部、财务部、技术部等
- 每个部门有自己的员工(服务)和职责(控制器)
- 部门之间可以相互合作(导入/导出)
- 有些资源可以共享给其他部门使用
在 NestJS 中,模块(Module) 就是这样的"部门",它是应用程序的基本组织单元。
模块的基本结构
1. 最简单的模块
typescript
import { Module } from '@nestjs/common';
@Module({
controllers: [], // 控制器:处理HTTP请求
providers: [], // 服务提供者:业务逻辑
imports: [], // 导入其他模块
exports: [], // 导出给其他模块使用
})
export class AppModule {}
2. 实际的模块示例
typescript
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController], // 这个模块的控制器
providers: [CatsService], // 这个模块的服务
})
export class CatsModule {}
模块的四个核心属性详解
1. Controllers(控制器)
控制器负责处理 HTTP 请求:
typescript
// cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Get()
findAll() {
return this.catsService.findAll();
}
@Post()
create(@Body() createCatDto: any) {
return this.catsService.create(createCatDto);
}
}
// cats.module.ts
@Module({
controllers: [CatsController], // 注册控制器
providers: [CatsService],
})
export class CatsModule {}
2. Providers(服务提供者)
服务提供者包含业务逻辑:
typescript
// cats.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
private cats = [];
findAll() {
return this.cats;
}
create(cat: any) {
this.cats.push(cat);
return cat;
}
}
// cats.module.ts
@Module({
controllers: [CatsController],
providers: [CatsService], // 注册服务
})
export class CatsModule {}
3. Imports(导入)
导入其他模块来使用它们的服务:
typescript
// database.module.ts
@Module({
providers: [DatabaseService],
exports: [DatabaseService], // 导出服务供其他模块使用
})
export class DatabaseModule {}
// users.module.ts
@Module({
imports: [DatabaseModule], // 导入数据库模块
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
4. Exports(导出)
导出服务供其他模块使用:
typescript
// shared.module.ts
@Module({
providers: [SharedService, LoggerService],
exports: [SharedService, LoggerService], // 导出多个服务
})
export class SharedModule {}
// feature.module.ts
@Module({
imports: [SharedModule], // 现在可以使用 SharedService 和 LoggerService
controllers: [FeatureController],
providers: [FeatureService],
})
export class FeatureModule {}
实际应用示例
1. 用户管理模块
typescript
// user.entity.ts
export class User {
id: number;
name: string;
email: string;
}
// user.service.ts
import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
@Injectable()
export class UserService {
private users: User[] = [];
findAll(): User[] {
return this.users;
}
findById(id: number): User {
return this.users.find((user) => user.id === id);
}
create(userData: Partial<User>): User {
const user = {
id: Date.now(),
...userData,
} as User;
this.users.push(user);
return user;
}
update(id: number, updateData: Partial<User>): User {
const userIndex = this.users.findIndex((user) => user.id === id);
if (userIndex !== -1) {
this.users[userIndex] = { ...this.users[userIndex], ...updateData };
return this.users[userIndex];
}
return null;
}
delete(id: number): boolean {
const userIndex = this.users.findIndex((user) => user.id === id);
if (userIndex !== -1) {
this.users.splice(userIndex, 1);
return true;
}
return false;
}
}
// user.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
} from '@nestjs/common';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findById(parseInt(id));
}
@Post()
create(@Body() createUserDto: any) {
return this.userService.create(createUserDto);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateUserDto: any) {
return this.userService.update(parseInt(id), updateUserDto);
}
@Delete(':id')
delete(@Param('id') id: string) {
return this.userService.delete(parseInt(id));
}
}
// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService], // 导出服务供其他模块使用
})
export class UserModule {}
2. 文章管理模块(使用用户服务)
typescript
// post.entity.ts
export class Post {
id: number;
title: string;
content: string;
authorId: number;
}
// post.service.ts
import { Injectable } from '@nestjs/common';
import { Post } from './post.entity';
import { UserService } from '../user/user.service'; // 使用用户服务
@Injectable()
export class PostService {
private posts: Post[] = [];
constructor(private userService: UserService) {}
findAll(): Post[] {
return this.posts;
}
findByAuthor(authorId: number): Post[] {
return this.posts.filter((post) => post.authorId === authorId);
}
create(postData: Partial<Post>): Post {
// 验证作者是否存在
const author = this.userService.findById(postData.authorId);
if (!author) {
throw new Error('作者不存在');
}
const post = {
id: Date.now(),
...postData,
} as Post;
this.posts.push(post);
return post;
}
getPostWithAuthor(postId: number) {
const post = this.posts.find((p) => p.id === postId);
if (!post) return null;
const author = this.userService.findById(post.authorId);
return {
...post,
author,
};
}
}
// post.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { PostService } from './post.service';
@Controller('posts')
export class PostController {
constructor(private postService: PostService) {}
@Get()
findAll() {
return this.postService.findAll();
}
@Get(':id/with-author')
getPostWithAuthor(@Param('id') id: string) {
return this.postService.getPostWithAuthor(parseInt(id));
}
@Get('author/:authorId')
findByAuthor(@Param('authorId') authorId: string) {
return this.postService.findByAuthor(parseInt(authorId));
}
@Post()
create(@Body() createPostDto: any) {
return this.postService.create(createPostDto);
}
}
// post.module.ts
import { Module } from '@nestjs/common';
import { PostController } from './post.controller';
import { PostService } from './post.service';
import { UserModule } from '../user/user.module'; // 导入用户模块
@Module({
imports: [UserModule], // 导入用户模块以使用 UserService
controllers: [PostController],
providers: [PostService],
})
export class PostModule {}
共享模块
1. 创建共享模块
typescript
// shared/logger.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class LoggerService {
log(message: string) {
console.log(`[${new Date().toISOString()}] ${message}`);
}
error(message: string, error?: any) {
console.error(`[${new Date().toISOString()}] ERROR: ${message}`, error);
}
}
// shared/config.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class ConfigService {
private config = {
apiUrl: 'https://api.example.com',
maxRetries: 3,
timeout: 5000,
};
get(key: string): any {
return this.config[key];
}
set(key: string, value: any): void {
this.config[key] = value;
}
}
// shared/shared.module.ts
import { Module } from '@nestjs/common';
import { LoggerService } from './logger.service';
import { ConfigService } from './config.service';
@Module({
providers: [LoggerService, ConfigService],
exports: [LoggerService, ConfigService], // 导出供其他模块使用
})
export class SharedModule {}
2. 在多个模块中使用共享模块
typescript
// user/user.service.ts
import { Injectable } from '@nestjs/common';
import { LoggerService } from '../shared/logger.service';
@Injectable()
export class UserService {
constructor(private logger: LoggerService) {}
findAll() {
this.logger.log('获取所有用户');
return this.users;
}
create(userData: any) {
this.logger.log(`创建用户: ${userData.name}`);
// 创建用户逻辑
}
}
// user/user.module.ts
@Module({
imports: [SharedModule], // 导入共享模块
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
// post/post.service.ts
import { Injectable } from '@nestjs/common';
import { LoggerService } from '../shared/logger.service';
import { ConfigService } from '../shared/config.service';
@Injectable()
export class PostService {
constructor(private logger: LoggerService, private config: ConfigService) {}
findAll() {
this.logger.log('获取所有文章');
const maxRetries = this.config.get('maxRetries');
// 使用配置
}
}
// post/post.module.ts
@Module({
imports: [SharedModule, UserModule], // 导入多个模块
controllers: [PostController],
providers: [PostService],
})
export class PostModule {}
全局模块
有些模块(如日志、配置)需要在整个应用中使用,可以设置为全局模块:
typescript
import { Module, Global } from '@nestjs/common';
import { LoggerService } from './logger.service';
@Global() // 设置为全局模块
@Module({
providers: [LoggerService],
exports: [LoggerService],
})
export class GlobalModule {}
使用全局模块后,其他模块不需要导入就可以使用:
typescript
// 不需要在 imports 中添加 GlobalModule
@Module({
controllers: [SomeController],
providers: [SomeService], // SomeService 可以直接注入 LoggerService
})
export class SomeModule {}
动态模块
1. 创建动态模块
typescript
// database/database.module.ts
import { Module, DynamicModule } from '@nestjs/common';
import { DatabaseService } from './database.service';
@Module({})
export class DatabaseModule {
static forRoot(config: {
host: string;
port: number;
database: string;
}): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: 'DATABASE_CONFIG',
useValue: config,
},
DatabaseService,
],
exports: [DatabaseService],
};
}
static forFeature(entities: any[]): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: 'ENTITIES',
useValue: entities,
},
],
};
}
}
2. 使用动态模块
typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { UserModule } from './user/user.module';
@Module({
imports: [
DatabaseModule.forRoot({
host: 'localhost',
port: 5432,
database: 'myapp',
}),
UserModule,
],
})
export class AppModule {}
// user/user.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { User } from './user.entity';
@Module({
imports: [
DatabaseModule.forFeature([User]), // 为特定功能配置
],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
模块的最佳实践
1. 模块组织结构
src/
├── app.module.ts
├── shared/
│ ├── shared.module.ts
│ ├── logger.service.ts
│ └── config.service.ts
├── user/
│ ├── user.module.ts
│ ├── user.controller.ts
│ ├── user.service.ts
│ └── user.entity.ts
├── post/
│ ├── post.module.ts
│ ├── post.controller.ts
│ ├── post.service.ts
│ └── post.entity.ts
└── auth/
├── auth.module.ts
├── auth.controller.ts
├── auth.service.ts
└── auth.guard.ts
2. 根模块的组织
typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { PostModule } from './post/post.module';
import { AuthModule } from './auth/auth.module';
import { SharedModule } from './shared/shared.module';
@Module({
imports: [SharedModule, AuthModule, UserModule, PostModule],
})
export class AppModule {}
3. 功能模块的组织
typescript
// feature/feature.module.ts
import { Module } from '@nestjs/common';
import { FeatureController } from './feature.controller';
import { FeatureService } from './feature.service';
import { SharedModule } from '../shared/shared.module';
@Module({
imports: [SharedModule],
controllers: [FeatureController],
providers: [FeatureService],
exports: [FeatureService], // 如果其他模块需要使用
})
export class FeatureModule {}
命令行工具
1. 创建模块
bash
# 创建一个新模块
nest generate module users
# 或简写
nest g module users
# 创建完整的CRUD资源(包含模块、控制器、服务)
nest generate resource users
# 或简写
nest g resource users
2. 生成的文件结构
src/
├── users/
│ ├── users.module.ts
│ ├── users.controller.ts
│ ├── users.service.ts
│ └── dto/
│ ├── create-user.dto.ts
│ └── update-user.dto.ts
常见问题和解决方案
1. 循环依赖问题
typescript
// ❌ 错误:会导致循环依赖
// user.module.ts
@Module({
imports: [PostModule],
exports: [UserService],
})
export class UserModule {}
// post.module.ts
@Module({
imports: [UserModule],
exports: [PostService],
})
export class PostModule {}
// ✅ 解决方案1:使用 forwardRef
import { Module, forwardRef } from '@nestjs/common';
@Module({
imports: [forwardRef(() => PostModule)],
exports: [UserService],
})
export class UserModule {}
// ✅ 解决方案2:创建共享模块
@Module({
providers: [UserService, PostService],
exports: [UserService, PostService],
})
export class SharedModule {}
2. 服务注入问题
typescript
// ❌ 错误:忘记导入模块
@Module({
controllers: [PostController],
providers: [PostService], // PostService 需要 UserService,但没有导入 UserModule
})
export class PostModule {}
// ✅ 正确:导入依赖的模块
@Module({
imports: [UserModule], // 导入 UserModule
controllers: [PostController],
providers: [PostService],
})
export class PostModule {}
3. 服务导出问题
typescript
// ❌ 错误:没有导出服务
@Module({
providers: [UserService], // 只在 providers 中声明,没有导出
})
export class UserModule {}
// ✅ 正确:导出服务
@Module({
providers: [UserService],
exports: [UserService], // 导出服务供其他模块使用
})
export class UserModule {}
总结
NestJS 模块是应用程序的基础构建块,它们:
- 组织代码 - 将相关的控制器、服务、和其他组件组织在一起
- 管理依赖 - 通过 imports 和 exports 管理模块之间的依赖关系
- 提供封装 - 每个模块都有自己的作用域
- 支持重用 - 模块可以在不同的地方重复使用
记住这些要点:
- 每个应用至少有一个根模块(AppModule)
- 使用
@Module()
装饰器定义模块 - 通过
imports
导入其他模块 - 通过
exports
导出服务供其他模块使用 - 合理组织模块结构,避免循环依赖
从简单的模块开始,逐步构建复杂的应用程序架构!