可扩展表现

Nest (NestJS)open in new window 是一个用于构建高效、可扩展的 Node.jsopen in new window 服务器端应用程序的开发框架。

怎么扩展?类似于前端组件化?🤔️

Nest.js 的可扩展性表现在多个方面,比如:模块化架构、依赖注入、中间件和拦截器、微服务支持等。有点懵?没关系,我们一个个来分析 🧐

模块化架构

如果把 Nest.js 比作火箭,那么模块就是火箭的各个部分,比如:发动机、推进器、回收舱等。这样,你可以像搭积木一样,单独修改或添加模块,而不会影响整个火箭的飞行。

src
├── ...
├── app.module.ts
├── modules
├────── rocket/
├────── engine/
├────── // ... 其他模块

上面就是 Nest.js 模块化架构的简单示例,将整个应用程序分成多个模块,每个模块都专注于特定的任务。如果你还想让火箭支持载人功能,那只需要添加一个新的房间模块,比如:capsule

依赖注入

类似于火箭需要不同的燃料和润滑剂,Nest.js 使用依赖注入来管理组件之间的关系。这使得你能够像给火箭引擎添加燃料一样,轻松地管理组件之间的依赖。

听起来是不是有点抽象?🤔️

实际上,依赖注入的核心理念是解耦组件的依赖关系。这意味着它从组件内部获取依赖项,而不是在组件内部直接创建或实例化它们。通过这种方式,组件无需担心依赖项的创建和生命周期管理,而是通过依赖注入容器自动解析并提供所需的依赖项。

@Injectable()
class RrocketService {
  private engine;

  constructor(engine) {
    this.engine = engine;
  }
  // 使用 this.engine 进行操作
}

上面就是依赖注入的一个简单示例,通过 @Injectable() 装饰器,Nest.js 会将 Engine 注入到 RrocketService 中。开发者无需直接实例化 Engine,而是通过依赖注入容器来提供。

如果还是不明白依赖注入为什么可以增强 Nest.js 的可扩展性的话,我们再举个例子 🌰

星际飞船的维护: 假设你是星际飞船的首席设计师,星际飞船有多个系统,比如引擎、能源控制、生命支持等。每个系统都是一个组件,而星际飞船就是一个巨大的模块。

传统开发方式:在没有依赖注入的情况下,你可能会在每个系统内部创建或实例化它所依赖的其他系统。比如,在引擎系统内部,你需要直接实例化能源控制系统:

class Engine {
  constructor() {
    this.energyControl = new EnergyControl(); // 直接创建依赖项
  }
}

这样的问题在于,如果你想添加一个新的系统,比如防御系统,你就需要修改引擎的代码并在其中加入防御系统的实例化。这样一来,系统之间的关系就会变得紧密,代码也变得难以维护和扩展。

使用依赖注入:现在,假设你使用依赖注入。每个系统不再负责自己依赖项的实例化,而是通过依赖注入容器来提供。

@Injectable()
class Engine {
  this.energyControl;

  constructor(energyControl) {
    this.energyControl = energyControl; // 通过依赖注入容器提供依赖项
  }
}

如果你想添加防御系统,只需在依赖注入容器中注册防御系统,而无需修改引擎的代码。这使得系统之间的耦合度降低,每个系统变得更加独立,更容易被替换或添加。

中间件和拦截器

Nest.js 提供了中间件和拦截器的机制,使得在请求的处理过程中可以插入自定义的逻辑。中间件和拦截器可以用于实现诸如权限验证、日志记录、缓存等功能。通过这些机制,你可以在应用程序的不同层面上进行扩展,而不必改变核心的业务逻辑。

import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req, res, next) {
    // 在这里编写身份验证逻辑
    if (!req.headers.authorization) {
      // 如果请求头中没有授权信息,则返回未经授权的响应
      res.status(401).json({ message: 'Unauthorized' });
      return;
    }

    // 如果身份验证通过,则继续请求处理
    next();
  }
}

上述例子展示了一个中间件的实现,它会在请求处理过程中验证请求头中的授权信息。若请求头中缺少授权信息,将返回未经授权的响应;反之,若身份验证通过,则继续请求处理。这与前端的路由守卫类似,让你能够在不同层面上灵活地扩展应用程序的功能。

看图更好理解一点 ⬇️

微服务集成

Nest.js 在构建可扩展应用程序时提供了强大的微服务集成能力。通过微服务,你可以将应用程序拆分成独立的服务,使得它们可以独立开发、部署和维护。这种模块化的架构为应对不断变化的需求提供了灵活性。

当使用 Nest.js 构建微服务时,一个简单的例子是通过 HTTP 进行通信。以下是一个简化的例子:

  1. 创建一个微服务模块:
// cats.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';

@Module({
  controllers: [CatsController],
})
export class CatsModule {}
  1. 创建一个微服务控制器:
// cats.controller.ts

import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  getCats(): string {
    return 'Meow!';
  }
}
  1. 在主应用程序中引入微服务模块:
// app.module.ts

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}
  1. 启动微服务:
// 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();

在这个示例中,我们创建了一个简单的 CatsController,它有一个基本的 getCats 方法返回 "Meow!"。我们通过 HTTP 提供此微服务。

启动应用程序后,你可以通过浏览器或任何 HTTP 客户端向 http://localhost:3000/cats 发送请求,它将返回 "Meow!"。

这是一个简单的微服务示例,实际应用中可能会使用更复杂的通信模式和工具,具体取决于你的应用需求。