3. NestJS框架基础:模块、控制器、服务、依赖注入与装饰器模式

NestJS 是构建高效、可扩展 Node.js 服务端应用的首选框架。它通过结合 面向对象编程(OOP)、函数式编程(FP) 和 函数式响应式编程(FRP) 的思想,为开发者提供了类似 Spring 框架的模块化架构。本章将深入剖析 NestJS 的五大核心构造块:模块、控制器、服务、依赖注入与装饰器模式,为后续实现领域驱动设计与限流熔断的协同奠定坚实基础。

3.1 核心构造块总览

NestJS 应用由多个模块(Module)组成,每个模块封装了相关的控制器(Controller)、服务(Provider)和领域逻辑。下图展示了 NestJS 应用的基本分层与组件协作关系:

NestJS 核心构造块架构图 NestJS 核心构造块架构图 客户端请求 控制器 (Controller) @Controller() 路由处理 服务 (Service/Provider) @Injectable() 业务逻辑 模块 (Module) @Module() 组织与依赖声明 装饰器模式驱动 依赖注入 (DI) 容器
架构要点: NestJS 遵循“模块即领域边界”的设计哲学。每个模块可以看作一个微型的应用边界,内部封装了控制器(处理输入/输出)、服务(执行业务规则)和领域模型。这种分层天然契合 DDD 的限界上下文(Bounded Context)概念。

3.2 模块(Module)—— 组织的原子单位

模块是 NestJS 组织代码的基本单元,使用 @Module() 装饰器标记。每个模块负责声明其内部的控制器、服务以及需要导入的其他模块。

3.2.1 模块的核心属性

属性 作用 典型值
controllers 注册该模块下的控制器实例 [OrderController]
providers 注册可被注入的服务、工厂、仓库等 [OrderService, PaymentGateway]
imports 导入其他模块,共享其导出的 providers [DatabaseModule, LoggerModule]
exports 将本模块的部分 providers 暴露给其他模块 [OrderService]
// 订单模块示例
import { Module } from '@nestjs/common';
import { OrderController } from './order.controller';
import { OrderService } from './order.service';
import { PaymentModule } from '../payment/payment.module';

@Module({
  imports: [PaymentModule],          // 导入支付模块
  controllers: [OrderController],    // 注册控制器
  providers: [OrderService],         // 注册服务
  exports: [OrderService],           // 导出服务供其他模块使用
})
export class OrderModule {}
DDD 映射: 在领域驱动设计中,一个模块通常对应一个限界上下文(如“订单上下文”)。模块的 imports 和 exports 机制,恰好实现了上下文之间的依赖关系与防腐层(Anti-Corruption Layer)的集成点。

3.3 控制器(Controller)—— 请求入口与路由

控制器负责处理传入的 HTTP 请求并返回响应。它通过装饰器定义路由、请求方法和参数绑定,不包含任何业务逻辑。

3.3.1 控制器核心装饰器

装饰器 用途 示例
@Controller('path') 定义控制器及基础路由前缀 @Controller('orders')
@Get() 处理 GET 请求 @Get(':id')
@Post() 处理 POST 请求 @Post()
@Body() 提取请求体 @Body() createDto: CreateOrderDto
@Param() 提取路由参数 @Param('id') id: string
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { OrderService } from './order.service';
import { CreateOrderDto } from './dto/create-order.dto';

@Controller('orders')
export class OrderController {
  constructor(private readonly orderService: OrderService) {}

  @Post()
  async create(@Body() createDto: CreateOrderDto) {
    return this.orderService.createOrder(createDto);
  }

  @Get(':id')
  async findOne(@Param('id') id: string) {
    return this.orderService.getOrderById(id);
  }
}
职责边界: 控制器只做“翻译”工作——将 HTTP 请求转换为服务调用,并将服务返回的结果序列化为 HTTP 响应。任何业务判断、数据校验(除基础格式外)都应下沉到服务层或领域层。

3.4 服务(Provider)—— 业务逻辑的载体

在 NestJS 中,几乎所有可被注入的类都可以称为 Provider,其中最典型的是 Service。服务使用 @Injectable() 装饰器标记,由 NestJS 的依赖注入容器管理其生命周期。

3.4.1 服务的注册方式

除了标准类注册,NestJS 还支持多种 Provider 注册模式,以适应不同的场景需求:

注册方式 适用场景 示例
标准类 大多数业务服务 providers: [OrderService]
值提供者 注入常量或配置对象 { provide: 'CONFIG', useValue: config }
工厂提供者 需要动态创建或异步初始化 { provide: 'DB', useFactory: () => createDb() }
别名提供者 为现有 provider 创建别名 { provide: 'AliasService', useExisting: OrderService }
import { Injectable } from '@nestjs/common';

@Injectable()
export class OrderService {
  private orders: Map<string, any> = new Map();

  createOrder(dto: CreateOrderDto) {
    const order = { id: Date.now().toString(), ...dto, status: 'PENDING' };
    this.orders.set(order.id, order);
    return order;
  }

  getOrderById(id: string) {
    return this.orders.get(id);
  }
}

3.5 依赖注入(DI)—— 解耦的核心机制

依赖注入是 NestJS 实现控制反转(IoC)的核心手段。它允许开发者声明依赖关系,而无需手动管理对象的创建和生命周期。

3.5.1 DI 的工作流程

  1. 注册: 在模块的 providers 中注册服务类。
  2. 声明: 在构造函数中通过类型声明依赖(如 constructor(private svc: OrderService))。
  3. 解析: NestJS 的 DI 容器自动查找并实例化所有依赖,递归处理依赖链。
  4. 注入: 将解析好的实例注入到目标类的构造函数中。
// DI 容器自动处理依赖链
@Injectable()
export class PaymentService {
  constructor(private readonly httpService: HttpService) {} // 注入 HttpService
}

@Module({
  providers: [PaymentService, HttpService], // 注册所有依赖
})
export class PaymentModule {}
作用域控制: NestJS 支持三种 Provider 作用域:DEFAULT(单例,整个应用共享)、REQUEST(每个请求创建新实例)、TRANSIENT(每次注入创建新实例)。在限流熔断场景中,通常使用 DEFAULT 作用域来维护全局的熔断器状态。

3.6 装饰器模式——NestJS 的语法基石

装饰器模式是 NestJS 框架的底层设计模式。它允许在不修改类本身的情况下,动态地为类、方法、属性或参数添加元数据和行为。

3.6.1 装饰器分类与用途

装饰器类型 NestJS 示例 作用
类装饰器 @Module(), @Controller(), @Injectable() 标记类为特定构造块,附加元数据
方法装饰器 @Get(), @Post(), @UseGuards() 定义路由、HTTP 方法、守卫、拦截器
参数装饰器 @Body(), @Param(), @Query() 提取请求中的特定数据
属性装饰器 @Inject() 显式指定注入的 token

3.6.2 自定义装饰器实战

在限流熔断场景中,我们可以创建自定义装饰器来简化限流逻辑的注入:

import { SetMetadata } from '@nestjs/common';

// 定义限流元数据 key
export const RATE_LIMIT_KEY = 'rate_limit';

// 创建自定义装饰器
export const RateLimit = (limit: number, ttl: number) =>
  SetMetadata(RATE_LIMIT_KEY, { limit, ttl });

// 在控制器中使用
@Controller('orders')
export class OrderController {
  @Post()
  @RateLimit(10, 60) // 60 秒内最多 10 次请求
  async create(@Body() dto: CreateOrderDto) {
    // ...
  }
}
设计模式价值: 装饰器模式让 NestJS 实现了“声明式编程”。开发者只需通过注解声明“做什么”,框架负责在运行时“怎么做”。这种模式与 DDD 中的“领域事件”和“规范模式”天然契合,使得业务规则可以以装饰器形式附着在代码上,保持领域模型的纯净。

3.7 本章小结

NestJS 的模块、控制器、服务、依赖注入与装饰器模式共同构成了一个高度解耦、可测试的架构体系:

  • 模块 作为领域边界,组织相关功能并管理依赖关系。
  • 控制器 作为 HTTP 适配器,处理输入输出转换。
  • 服务 承载核心业务逻辑,通过 DI 实现松耦合。
  • 依赖注入 自动管理对象生命周期,提升可测试性。
  • 装饰器模式 提供声明式元数据编程能力,是框架扩展性的基石。