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 服务:
- 创建项目:
nest new my-project(使用 Nest CLI) - 生成模块:
nest generate module user - 生成控制器:
nest generate controller user - 生成服务:
nest generate service user - 编写业务逻辑:在 Service 中实现数据操作,在 Controller 中调用。
- 启动服务:
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 数据。
2.5 模块化与 DI 对架构演进的意义
理解 NestJS 的模块化和 DI 机制,是后续实现熔断降级、服务治理和架构演进的基础。例如:
- 熔断器作为提供者:我们可以将熔断器逻辑封装为一个
@Injectable()的CircuitBreakerService,通过 DI 注入到需要保护的服务中。 - 模块隔离:熔断降级策略可以按模块粒度配置,不同模块可以拥有独立的熔断阈值和恢复策略。
- 可替换性:由于 DI 的存在,我们可以轻松地将同步调用替换为带有熔断保护的异步调用,甚至替换为 Mock 服务进行测试。
架构启示:NestJS 的模块化设计天然支持防腐层和适配器模式。在引入熔断降级时,我们可以在模块边界处插入熔断逻辑,而无需侵入核心业务代码。