工厂模式与 Nest.js 核心思想 —— 从蜜雪冰城到企业级架构

工厂模式与 Nest.js 核心思想 ------ 从蜜雪冰城到企业级架构

本篇通过蜜雪冰城的真实故事,带你理解工厂模式的设计哲学,并以此切入 Nest.js 的模块、控制器、服务、依赖注入与装饰器,彻底搞懂一个 Nest 应用是如何"活"起来的。


一、为什么需要设计模式?从"点奶茶"说起

笔记里有一句话:

"你想喝奶茶,不用自己做(流程代码NO)找蜜雪冰城。"

这其实就是面向接口编程 的朴素体现。作为消费者,我们不关心奶茶的制作流程(放多少茶、加多少糖),只需要知道"蜜雪冰城"这个工厂能提供我们想要的产品。

在代码世界,如果我们直接 new IceCream()new LemonTea(),每新增一种产品,所有使用的地方都要修改。这就是紧耦合

工厂模式 应运而生:把对象的创建逻辑封装到一个工厂类中,使用者只需要告诉工厂"我要什么类型",工厂返回对应的实例。这正是笔记中 MixueFactory 的代码所演示的。

javascript 复制代码
class MixueFactory {
    static create(type) {
        switch(type) {
            case 'ice': return new IceCream();
            case 'lemon': return new LemonTea();
            case 'milk': return new MilkTea();
        }
    }
}

这样带来的好处

  • 使用者(开发者)只需记住 create('ice') 即可,不需要知道 IceCream 类的内部细节。
  • 如果未来产品构造方式变了(比如增加参数),只需要修改工厂类,所有调用处无需改动。
  • 所有产品类都实现了相同的 show() 方法,保证了行为的一致性。

笔记中的关键句

"开发者只需要调用 MixueFactory.create('type') 即可。由于工厂里的每个类都实现了相同的 show 接口,由工厂类生产出来的类,可以放心的直接调用。"


二、Nest.js 中的工厂模式:NestFactory

在 Nest.js 中,main.ts 里我们看到了这样的代码:

typescript 复制代码
const app = await NestFactory.create(AppModule);

NestFactory 就是一个工厂类 ,它负责创建整个 Nest 应用实例。我们不需要关心内部如何初始化中间件、如何配置底层 HTTP 服务器(Express/Fastify),只需要传入根模块 AppModule,就能得到一个可运行的应用对象。

这完美对应了蜜雪冰城的故事

  • 蜜雪冰城工厂 → NestFactory
  • 点一杯柠檬水 → NestFactory.create(AppModule)
  • 工厂返回产品实例 → 返回 app 对象,我们可以调用 app.listen(3000)

三、Nest 的模块化 ------ @Module 装饰器解读

笔记里写道:

"Module 是一个整体,后端最常见的 MVC 模式。一个文件几千行代码,组织控制器 controller, service 层 CRUD。"

Nest 用 @Module() 装饰器把控制器、服务组织成一个逻辑单元。来看 app.module.ts 的代码:

typescript 复制代码
@Module({
  imports: [], // 依赖外界?
  controllers: [AppController], // 控制器 校验,简单逻辑,
  providers: [AppService], // data service 复杂业务 
})
export class AppModule {}

对注释的深度解读

  • imports: [] // 依赖外界?

    这个数组用于导入其他模块。比如我们后续会导入 TodosModule,这意味着本模块需要用到其他模块提供的服务。它就像是"我要从隔壁部门借调人手",只有导入的模块在 exports 中暴露了服务,当前模块才能使用。

  • controllers: [AppController] // 控制器 校验,简单逻辑

    控制器只负责"接客" ------ 接收请求、提取参数、调用服务、返回响应。**"校验"严格来说是由 ValidationPipe 管道完成的,控制器本身不写校验逻辑,但它是校验的"入口"。"简单逻辑"**意味着控制器里不能有复杂的业务计算,只能做路由调度。

  • providers: [AppService] // data service 复杂业务

    这里注册的是所有 @Injectable() 的类,它们负责真正的业务逻辑、数据库操作、复杂计算。这就是 Service 层 ,是 MVC 中的 Model(模型) 的体现,但 Nest 更倾向于称为 Provider。


四、控制器与服务 ------ 依赖注入的魔法

app.controller.ts 中:

typescript 复制代码
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
  // ...
}

这行代码的底层逻辑

  1. TypeScript 编译时,constructor 参数的类型信息被保留在元数据中(design:paramtypes)。
  2. Nest 在启动时扫描所有 @Controller() 类,发现 AppController 需要 AppService
  3. 它去 IoC 容器中查找是否已有 AppService 的实例(因为 AppService@Injectable() 标记,已在 providers 中注册)。
  4. 如果有,直接注入;如果没有,先创建再注入。
  5. 注入后,this.appService 就可以直接使用了。

这就是依赖注入(DI)的本质控制反转 ------ 不是我们主动去 new,而是容器把依赖"送"给我们。这大大降低了耦合,也方便了单元测试(可以注入 Mock 对象)。


五、装饰器模式 ------ @ 符号到底是什么?

笔记里说:

"装饰器模式在不修改原有对象的前提下,动态给对象添加叠加额外功能。"

但 Nest 中的 @Controller@Module@Injectable 并不是经典装饰器模式(运行时套壳),它们是 TypeScript 装饰器 ,本质上是一个高阶函数 ,在类定义时执行,用于给类添加元数据(metadata)。

例如 @Module({...}) 会在 AppModule 类上挂载配置信息,Nest 启动时会读取这些元数据,从而知道如何组装应用。

而 Nest 中真正的装饰器模式应用拦截器(Interceptor)守卫(Guard),它们会在运行时包裹路由处理函数,动态增加日志、权限等功能。这部分我们会在后续文章中深入。


六、从 main.ts 看应用启动流程

typescript 复制代码
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

流程拆解

  1. NestFactory.create(AppModule) ------ 工厂创建应用实例。
  2. 内部会读取 AppModule 的元数据,递归解析所有依赖模块(如 TodosModule)。
  3. 初始化所有控制器和服务,建立路由映射。
  4. 启动底层 HTTP 服务器,监听指定端口。
  5. 此时,我们的 API 就可以被访问了。

七、小结

概念 对应笔记/代码 我的理解
工厂模式 蜜雪冰城、NestFactory 封装对象创建,解耦调用方与实现类
模块 @Module({imports, controllers, providers}) 组织代码的单元,类似公司部门
控制器 @Controller,负责路由和参数提取 只做调度,不做业务
服务 @Injectable,负责业务逻辑 可被注入到任何需要的地方
依赖注入 constructor(private readonly xxxService) 容器自动提供依赖,无需手动 new
装饰器 @符号,提供元数据 给类/方法打标签,供框架读取
相关推荐
用户938515635071 小时前
实战 Todo CRUD —— 从路由到异常,手写一个完整模块
后端·typescript·nestjs
GetcharZp2 小时前
Qdrant 向量数据库 Golang 实战指南:从零构建高性能检索应用
后端
hsfxuebao2 小时前
OpenSpec 完整使用流程笔记 (SDD)
后端
唐青枫2 小时前
别把 struct 只当成数据盒子:Zig 结构体从入门到实战
后端
why技术3 小时前
AI 写的文章,可能都带着手敲一遍都去不掉的“隐形水印”。
前端·人工智能·后端
北斗落凡尘3 小时前
LangGraph 入门实战(9)--中断
后端·langchain
CodeSheep3 小时前
又一个华为天才少年,离职了!
前端·后端·程序员
IT_陈寒4 小时前
Vue的双向绑定把我坑惨了,原来这个场景不能用
前端·人工智能·后端
烬羽5 小时前
从 nest new 到 Hello World:一个最小 NestJS 项目,讲透工厂 + 装饰器 + 模块化
设计模式·node.js·nestjs