NestJS 是一个用于构建高效、可靠且可扩展的服务端应用程序的框架,它完全使用 TypeScript 构建,并深受 Angular 的启发。它不仅是一个简单的 Web 框架,也是一套完整的后端开发解决方案。
NestJS 的核心架构:模块化与 MVC
在深入设计模式之前,我们先来了解 NestJS 的基本架构。它高度模块化,并遵循经典的 MVC(Model-View-Controller)思想,尽管在后端 API 开发中,View 层通常被 JSON 数据响应所取代。
一个典型的 NestJS 应用由以下几个核心部分组成:
-
模块 (
Module) :使用@Module()装饰器定义,是组织应用程序结构的基本单元。一个模块可以将相关的控制器(Controllers)和提供者(Providers,如 Services)封装在一起。AppModule是应用的根模块,NestJS 从这里开始构建应用依赖图。typescript
编辑
ts// app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TodosModule } from './todos/Todos.module'; @Module({ imports: [TodosModule], // 导入其他模块 controllers: [AppController], // 注册控制器 providers: [AppService], // 注册服务 }) export class AppModule {} -
控制器 (
Controller) :使用@Controller()装饰器定义,负责处理传入的请求并返回响应。它接收客户端的请求,进行初步的参数校验和逻辑处理,然后将复杂的业务逻辑委托给服务层。typescript
编辑
ts// app.controller.ts import { Controller, Get } from '@nestjs/common'; import { AppService } from './app.service'; @Controller() export class AppController { constructor(private readonly appService: AppService) {} @Get() getHello(): string { return this.appService.getHello(); } } -
提供者/服务 (
Provider/Service) :使用@Injectable()装饰器定义,是 NestJS 中一个非常重要的概念。服务层封装了具体的业务逻辑,例如数据库操作(CRUD)、复杂的计算或与其他服务的交互。控制器通过依赖注入来使用服务。typescript
编辑
ts// app.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class AppService { getHello(): string { return 'Hello World!'; } } -
入口文件 (
main.ts) :这是应用的起点。它使用NestFactory来创建一个 Nest 应用实例,并监听指定的端口。typescript
编辑
ts// main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
工厂模式:解耦对象创建的利器
工厂模式 是 NestJS 中最基础也是最重要的设计模式之一。它的核心思想是将对象的创建过程封装起来,使得客户端代码无需关心对象的具体创建细节,只需向"工厂"请求即可。
这就像你想喝奶茶,不需要自己去买茶叶、牛奶、糖然后一步步制作,而是直接去"蜜雪冰城"(工厂)点一杯。工厂会根据你的需求(参数)生产出你想要的奶茶(对象)。
在 NestJS 中,工厂模式的应用随处可见:
- 应用实例的创建 :在
main.ts中,NestFactory.create(AppModule)就是一个典型的工厂方法。我们不需要知道 Nest 应用实例内部是如何被复杂地构建和配置的,只需要调用这个静态方法,就能得到一个 ready-to-use 的应用实例。 - 依赖注入系统 :NestJS 的依赖注入(DI)容器本质上就是一个强大的工厂。当你在控制器的构造函数中声明
private readonly appService: AppService时,NestJS 的 DI 容器(工厂)会自动为你创建或提供一个AppService的实例。你无需手动new AppService(),这极大地降低了类与类之间的耦合度。
代码示例:一个简化的工厂模式
假设我们有一个 MixueFactory,它可以根据类型生产不同的饮品。
typescript
编辑
js
// 抽象产品
interface Drink {
show(): void;
}
// 具体产品
class MilkTea implements Drink {
show() { console.log('这是一杯奶茶'); }
}
class LemonTea implements Drink {
show() { console.log('这是一杯柠檬水'); }
}
// 工厂类
class MixueFactory {
static create(type: string): Drink {
if (type === 'milktea') {
return new MilkTea();
} else if (type === 'lemontea') {
return new LemonTea();
}
throw new Error('未知饮品类型');
}
}
// 客户端使用
const myDrink = MixueFactory.create('milktea');
myDrink.show(); // 输出: 这是一杯奶茶
通过这种方式,如果未来要增加一种新的饮品(例如咖啡),我们只需要添加新的产品类并修改工厂类,而所有调用 MixueFactory.create 的客户端代码都无需改动。这完美体现了"开闭原则"。
装饰器模式:为类动态添加"超能力"
装饰器模式是 NestJS 的灵魂,它让框架的声明式编程风格成为可能。装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。
它的核心作用是在不修改原始对象代码的前提下,动态地给对象添加额外的功能或元数据。你可以把它想象成给一个普通的蛋糕(类)加上奶油、水果和蜡烛(装饰器),让它变成一个生日蛋糕,而蛋糕本身还是那个蛋糕。
在 NestJS 中,装饰器被用到了极致,几乎所有的核心功能都通过装饰器来定义:
@Module(): 将一个类标记为模块,并提供元数据来组织应用的各个部分。@Controller('todos'): 将一个类定义为控制器,并为其指定一个路由前缀(如/todos)。@Get(),@Post(),@Put(),@Delete(): 将这些装饰器附加到控制器的方法上,将其绑定到特定的 HTTP 请求方法和路径。@Injectable(): 标记一个类可以被 NestJS 的依赖注入系统管理。@Param(),@Body(),@Query(): 用于从 HTTP 请求中提取特定的数据,并自动注入到控制器方法的参数中。
代码示例:解析装饰器的魔力
让我们看一个 TodosController 的例子,来理解装饰器是如何工作的。
typescript
编辑
ts
// Todos.controller.ts
import { Controller, Get, Post, Param, Body } from '@nestjs/common';
import { TodosService } from './Todos.service';
@Controller('todos') // 1. 定义基础路由为 /todos
export class TodosController {
constructor(private readonly todosService: TodosService) {}
@Get() // 2. 处理 GET 请求到 /todos
findAll() {
return this.todosService.findAll();
}
@Post() // 3. 处理 POST 请求到 /todos
create(@Body('title') title: string) { // 4. 从请求体中提取 title 字段
return this.todosService.create(title);
}
@Get(':id') // 5. 处理 GET 请求到 /todos/:id
findOne(@Param('id') id: string) { // 6. 从路由参数中提取 id
return this.todosService.findOne(Number(id));
}
}
@Controller('todos')告诉 NestJS,这个类是一个控制器,所有内部方法的路由都以/todos开头。@Get()告诉 NestJS,findAll方法用来处理对/todos的 GET 请求。@Body('title')是一个参数装饰器,它自动从 POST 请求的 JSON 体中取出title字段的值,并传递给create方法。
如果没有装饰器,我们需要编写大量重复的、命令式的代码来手动注册路由、解析请求对象,代码会变得非常冗长和难以维护。装饰器模式让代码变得极其简洁、清晰和声明式,开发者可以更专注于业务逻辑本身。
总结
NestJS 的强大之处在于它巧妙地运用了经典的设计模式来构建一个优雅、健壮且易于维护的框架。
- 工厂模式 通过
NestFactory和依赖注入系统,实现了对象的创建与使用的解耦,让代码结构更清晰,更易于测试和管理。 - 装饰器模式作为框架的基石,提供了声明式的元数据,极大地简化了路由、依赖注入等核心功能的配置,让开发者能够以一种非常直观的方式来构建复杂的后端应用。
理解这两种模式,是掌握 NestJS 精髓的关键一步。