2. NestJS 快速回顾:模块化设计、依赖注入、提供者与控制器、基础HTTP服务搭建

在深入熔断降级与架构演进之前,有必要快速回顾 NestJS 的核心设计理念与基础构建块。NestJS 的架构深受 Angular 启发,围绕模块化、依赖注入和装饰器语法构建,为开发者提供了一种高度结构化、可测试且可扩展的后端开发范式。

2.1 模块化设计:应用的骨架

模块(Module)是 NestJS 组织代码的基本单元。每个应用至少有一个根模块(AppModule),通过模块的导入/导出机制,可以清晰地划分功能边界,实现关注点分离。

核心原则:模块是提供者(Provider)和控制器(Controller)的编译上下文。同一个作用域内的提供者默认共享实例(单例模式)。
模块属性 作用 说明
imports 导入其他模块 将其他模块导出的提供者引入当前模块上下文
controllers 注册控制器 处理传入请求并返回响应
providers 注册提供者 服务、仓库、工厂等可被注入的依赖
exports 导出提供者 使其他模块可以注入本模块的提供者
// app.module.ts - 根模块示例
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';

@Module({
  imports: [UserModule],          // 导入用户模块
  controllers: [],                // 根模块通常不直接注册控制器
  providers: [],                  // 全局服务可在此注册
  exports: [],
})
export class AppModule {}

2.2 依赖注入:解耦与可测试性

NestJS 内置了强大的依赖注入(DI)容器。提供者通过 @Injectable() 装饰器标记,由 DI 容器负责实例化并注入到需要它的类中。这种机制极大地降低了组件间的耦合度,使得单元测试和模块替换变得异常简单。

DI 工作流: 注册(在模块的 providers 中声明)→ 解析(DI 容器分析构造函数参数)→ 注入(自动创建实例并注入)。
// user.service.ts - 可注入的服务
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  private users = [{ id: 1, name: 'Alice' }];

  findAll() {
    return this.users;
  }
}

// user.controller.ts - 消费该服务
import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {} // 依赖注入

  @Get()
  getAll() {
    return this.userService.findAll();
  }
}

2.3 提供者与控制器:请求处理管道

在 NestJS 中,控制器负责路由和请求/响应处理,提供者(通常是 Service)负责业务逻辑。这种分离使得每一层职责单一,便于维护和演进。

  • 控制器(Controller):使用 @Controller() 装饰器定义路由前缀,内部方法通过 @Get()、@Post() 等装饰器绑定 HTTP 方法。
  • 提供者(Provider):使用 @Injectable() 装饰器,可被注入到控制器或其他提供者中。常见的提供者类型包括 Service、Repository、Factory、Guard 等。
常见误区:不要在控制器中编写复杂的业务逻辑。控制器应保持“薄”层,仅负责参数提取、调用服务、返回结果。业务逻辑应封装在提供者中。

2.4 基础 HTTP 服务搭建:从零到运行

以下步骤演示如何快速搭建一个具备模块化结构的 NestJS HTTP 服务:

  1. 创建项目:nest new my-project(使用 Nest CLI)
  2. 生成模块:nest generate module user
  3. 生成控制器:nest generate controller user
  4. 生成服务:nest generate service user
  5. 编写业务逻辑:在 Service 中实现数据操作,在 Controller 中调用。
  6. 启动服务:npm run start:dev(热重载模式)
// 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], // 如果其他模块需要注入 UserService
})
export class UserModule {}

启动后,访问 http://localhost:3000/users 即可看到返回的用户列表 JSON 数据。

NestJS 核心架构组件关系图 @Module 模块 @Controller 控制器 @Injectable 提供者 @Get / @Post 路由 业务逻辑 / 数据访问 依赖注入调用 模块是组织单元,控制器处理请求,提供者封装逻辑,DI 容器负责实例化与注入

2.5 模块化与 DI 对架构演进的意义

理解 NestJS 的模块化和 DI 机制,是后续实现熔断降级、服务治理和架构演进的基础。例如:

  • 熔断器作为提供者:我们可以将熔断器逻辑封装为一个 @Injectable() 的 CircuitBreakerService,通过 DI 注入到需要保护的服务中。
  • 模块隔离:熔断降级策略可以按模块粒度配置,不同模块可以拥有独立的熔断阈值和恢复策略。
  • 可替换性:由于 DI 的存在,我们可以轻松地将同步调用替换为带有熔断保护的异步调用,甚至替换为 Mock 服务进行测试。
架构启示:NestJS 的模块化设计天然支持防腐层和适配器模式。在引入熔断降级时,我们可以在模块边界处插入熔断逻辑,而无需侵入核心业务代码。
模块化与 DI 对熔断降级的支撑关系 业务模块 UserModule OrderModule PaymentModule 熔断降级层 CircuitBreakerService @Injectable() 状态机:CLOSED / OPEN / HALF_OPEN 降级策略:Fallback / Cache / Mock 外部依赖 第三方 API 数据库 微服务 B 消息队列 DI 注入 受保护调用 模块化隔离业务边界,DI 容器将熔断器注入到调用链中,实现无侵入的降级保护

2.6 本章小结