Skip to content

NestJS 核心架构

模块化设计、依赖注入与装饰器模式

目录


模块化设计

核心概念

┌─────────────────────────────────────────────────────────┐
│                      Module                              │
├─────────────────────────────────────────────────────────┤
│                                                          │
│   ┌─────────────┐    ┌─────────────┐    ┌──────────┐   │
│   │ Controllers │ ←──│  Providers  │←───│  Imports  │   │
│   └─────────────┘    └─────────────┘    └──────────┘   │
│         │                   │                           │
│         ▼                   ▼                           │
│   处理 HTTP 请求      业务逻辑/服务                       │
│                                                          │
│   ┌─────────────┐                                        │
│   │   Exports   │  →  暴露给其他模块使用                   │
│   └─────────────┘                                        │
│                                                          │
└─────────────────────────────────────────────────────────┘

模块定义

typescript
// users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DatabaseModule } from '../database/database.module';

@Module({
  imports: [DatabaseModule],           // 导入其他模块
  controllers: [UsersController],      // 该模块的控制器
  providers: [UsersService],           // 该模块的服务提供者
  exports: [UsersService]              // 暴露给外部模块
})
export class UsersModule {}

Controller 与 Service

typescript
// users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }
}

// users.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  private users = [];

  findAll() {
    return this.users;
  }

  findOne(id: number) {
    return this.users.find(u => u.id === id);
  }

  create(data: CreateUserDto) {
    const user = { id: Date.now(), ...data };
    this.users.push(user);
    return user;
  }
}

动态模块

typescript
// config.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common';

@Global()
@Module({})
export class ConfigModule {
  static forRoot(options: ConfigOptions): DynamicModule {
    return {
      module: ConfigModule,
      providers: [
        {
          provide: 'CONFIG_OPTIONS',
          useValue: options
        },
        ConfigService
      ],
      exports: [ConfigService]
    };
  }

  static forRootAsync(options: ConfigAsyncOptions): DynamicModule {
    return {
      module: ConfigModule,
      imports: options.imports || [],
      providers: [
        {
          provide: 'CONFIG_OPTIONS',
          useFactory: options.useFactory,
          inject: options.inject || []
        },
        ConfigService
      ],
      exports: [ConfigService]
    };
  }
}

// 使用
@Module({
  imports: [
    ConfigModule.forRootAsync({
      imports: [SecretsModule],
      inject: [SecretsService],
      useFactory: (secrets: SecretsService) => ({
        apiKey: secrets.get('API_KEY')
      })
    })
  ]
})
export class AppModule {}

依赖注入

Provider 类型

typescript
// 标准类 Provider
@Injectable()
export class UsersService {}

// 值 Provider
{
  provide: 'API_KEY',
  useValue: 'my-secret-key'
}

// 工厂 Provider
{
  provide: 'ASYNC_CONNECTION',
  useFactory: async (config: ConfigService) => {
    const connection = await createConnection(config.get('DB_URL'));
    return connection;
  },
  inject: [ConfigService]
}

// 类 Provider(别名)
{
  provide: 'AbstractLogger',
  useClass: process.env.NODE_ENV === 'production' 
    ? ProductionLogger 
    : DevelopmentLogger
}

// 已存在 Provider(别名)
{
  provide: 'AliasService',
  useExisting: UsersService
}

作用域

typescript
import { Injectable, Scope } from '@nestjs/common';

// 单例(默认)
@Injectable()
export class SingletonService {}

// 请求级别
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {}

// 瞬态(每次注入都创建新实例)
@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {}

循环依赖处理

typescript
// a.service.ts
@Injectable()
export class AService {
  constructor(
    @Inject(forwardRef(() => BService))
    private bService: BService
  ) {}
}

// b.service.ts
@Injectable()
export class BService {
  constructor(
    @Inject(forwardRef(() => AService))
    private aService: AService
  ) {}
}

装饰器原理

装饰器本质

typescript
// TypeScript 装饰器 = 函数
function Injectable(): ClassDecorator {
  return (target: Function) => {
    // 存储元数据
    Reflect.defineMetadata('injectable', true, target);
  };
}

// 获取元数据
const isInjectable = Reflect.getMetadata('injectable', SomeClass);

自定义装饰器

typescript
// 参数装饰器
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;
    return data ? user?.[data] : user;
  }
);

// 使用
@Get('profile')
getProfile(@User() user: UserEntity) {
  return user;
}

@Get('profile')
getUsername(@User('username') username: string) {
  return { username };
}
typescript
// 方法装饰器
import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

// 使用
@Post()
@Roles('admin')
create() {}

// 在 Guard 中读取
const roles = this.reflector.get<string[]>('roles', context.getHandler());
typescript
// 组合装饰器
import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';

export function Auth(...roles: string[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized' })
  );
}

// 使用
@Post()
@Auth('admin')
create() {}

请求生命周期

                    收到请求


               ┌───────────────┐
               │   Middleware   │  可访问 req/res
               └───────┬───────┘

               ┌───────────────┐
               │    Guards     │  决定是否放行
               └───────┬───────┘

               ┌───────────────┐
               │ Interceptors  │  前置逻辑
               │   (before)    │
               └───────┬───────┘

               ┌───────────────┐
               │    Pipes      │  参数转换/验证
               └───────┬───────┘

               ┌───────────────┐
               │   Controller  │  处理请求
               │    Handler    │
               └───────┬───────┘

               ┌───────────────┐
               │ Interceptors  │  后置逻辑
               │   (after)     │
               └───────┬───────┘

               ┌───────────────┐
               │Exception Filter│  异常处理
               └───────┬───────┘

                    响应发送

高频面试题

Q1: NestJS 的依赖注入是如何实现的?

  1. 装饰器标记@Injectable() 标记可注入类
  2. 元数据反射reflect-metadata 存储构造函数参数类型
  3. IoC 容器:扫描模块,创建并缓存实例
  4. 懒加载:按需实例化依赖
typescript
// 简化原理
function resolve(token) {
  if (container.has(token)) {
    return container.get(token);
  }
  
  const dependencies = Reflect.getMetadata('design:paramtypes', token);
  const instances = dependencies.map(dep => resolve(dep));
  const instance = new token(...instances);
  
  container.set(token, instance);
  return instance;
}

Q2: Module 的 forRoot 和 forRootAsync 区别?

方法配置方式适用场景
forRoot同步配置静态配置值
forRootAsync异步配置需要注入其他服务

Q3: 如何在 NestJS 中处理循环依赖?

  1. forwardRef:延迟解析依赖
  2. 重构:提取公共逻辑到第三个服务
  3. 模块重新分层:调整模块依赖关系

Q4: Provider 的作用域有哪些?

作用域说明使用场景
DEFAULT (Singleton)应用生命周期单例无状态服务
REQUEST每个请求创建新实例请求级上下文
TRANSIENT每次注入创建新实例无共享状态

项目结构推荐

src/
├── app.module.ts
├── main.ts
├── common/
│   ├── decorators/
│   ├── filters/
│   ├── guards/
│   ├── interceptors/
│   └── pipes/
├── config/
│   ├── config.module.ts
│   └── config.service.ts
├── users/
│   ├── users.module.ts
│   ├── users.controller.ts
│   ├── users.service.ts
│   ├── dto/
│   │   └── create-user.dto.ts
│   └── entities/
│       └── user.entity.ts
└── database/
    ├── database.module.ts
    └── database.providers.ts

前端面试知识库