🌑

Mocha's Blog

目录
  1. 什么是 NestJS
  2. NestJS 的架构
  3. 依赖注入与控制反转
  4. 环境管理
  5. CI/CD

用 NestJS 构建企业级、易部署的服务端应用

发布时间:2025年4月11日

pasted-image-1771767346941.webp

什么是 NestJS

NestJS 是一个用于构建高效且可伸缩的服务端应用的框架,有以下几个特点:

  1. 基于 Node.js 运行时,旨在成为一个「平台无关」的框架,开发者可以自由地切换其底层运行时,目前支持:express、fastify 两种
  2. 深受 Angular 的影响,提供一套全面的工具和特性,如:依赖注入、面向对象编程、函数式编程、模块化系统,旨在解决服务端 JavaScript 开发中缺乏有效架构的问题
  3. 不仅可以用来构建 HTTP 应用,也可以用来构建微服务,同时支持多种通信方式(TCP、gRPC、Redis、RabbitMQ 等)

因此,相较于市面上其他的 Node.js 框架,Nest.js 应用的架构设计充斥着严谨、理性的美,使其非常适合构建企业级应用 。

NestJS 的架构

pasted-image-1771766715855.webp

概念 作用 主要用途
Module 组织代码,定义应用的结构 将相关功能分组 (Controller、Provider)
Provider 提供可注入的服务(如业务逻辑、数据库操作) 封装可复用的逻辑 (Service、Repository、Helper)
Controller 处理客户端请求并返回响应,通过组合不同的 Service 形成业务逻辑 定义路由(REST API 端点)
Service 实现业务逻辑 处理复杂逻辑(如数据库查询、计算)
Pipe 转换或验证输入数据 参数校验(如 DTO)、数据转换(如字符串转数字)
Guard 决定请求是否允许访问(权限控制) 身份验证(JWT)、角色检查(RBAC)
Exception Filter 捕获并处理未捕获的异常 统一错误响应格式、日志记录
Middleware 在请求到达 Controller 前或响应返回前执行代码 日志记录、请求修改(如添加 Headers)、跨域处理(CORS)
Interceptor 在请求/响应流程中插入逻辑(AOP 风格) 日志记录、性能监控、响应数据转换

依赖注入与控制反转

在 NestJS 项目中,有两个概念充斥着整个项目:依赖注入( Dependency Injection, DI )、控制反转( Inversion of Control, IOC )

控制反转是一种设计原则,其中对象的行为控制权被反转或转移到对象外部 。程序的流程由框架或外部服务决定,而不是直接由程序员控制 。与传统编程中自定义代码调用可重用库来处理通用任务不同,使用控制反转时,外部代码或框架处于控制地位并调用自定义代码

控制反转的核心思想是:

将任务的执行与实现解耦,比如:我需要一把锤子,但我不想知道锤子是怎么造(构建)的

控制反转的思想其实很常见,比如:

  • Redux 的 connect:组件不关心 state、dispatch 是怎么注入的
  • 点击事件:你定义函数,但由外部(如浏览器、框架)决定何时调用它
  • 前端路由系统:你只需要声明路由和组件,不关心如何以及何时被渲染
  • ORM 框架:你定义数据模型,但由框架管理数据库连接和查询

依赖注入不是一种新的设计原则,它是一种实现控制反转的方式,因此他们的核心思想都是一样的,

比如我们有一个锤子类

class Hammer {
  hit() {
    console.log("敲钉子!");
  }
}

在过往的设计中我们使用时,可能是这样的

class Person {
    private hammer: Hammer;
    constructor() {
        this.hammer = new Hammer(); // 自己创建依赖
    }
}

但在 NestJS 中我们是这样使用的

@Injectable() // 声明一个可注入的锤子类
class Hammer {
  hit() {
    console.log("敲钉子!");
  }
}

@Controller()
class Person {
  constructor(private hammer: Hammer) {} // NestJS 自动注入 Hammer 实例

  @Get('hit')
  useHammer() {
    this.hammer.hit(); // 直接使用注入的依赖
  }
}

环境管理

一个功能完备的服务端应用通常会依赖许多外部资源,比如:Redis、数据库、网关等。为了保证应用在开发和生产环境中的稳定性和一致性,我们需要对这些依赖进行有效的管理和配置。常见的做法是通过配置文件统一管理这些外部依赖的连接信息,例如:资源地址、端口、用户名和密码等。同时,不同环境可能有不同的配置需求,因此我们通常会引入环境变量或配置管理工具,以便灵活适配各种场景。

为了简化外部依赖的部署和管理过程,Docker Compose 是一个非常实用的工具。通过 Docker Compose,可以将应用及其所需的外部依赖统一定义在一个配置文件中,并通过简单的命令实现一键启动。这种方式不仅能确保开发和生产环境的一致性,还能大幅降低环境配置的复杂度。在开发环境中,开发者可以快速拉起所有依赖服务,在生产环境中也能通过相同的配置文件实现高效部署。

下面就是我们项目在开发和生产环境中的 docker-compose.yaml

services:
  postgres:
    image: postgres:16-alpine
    container_name: orion-postgres
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: orion
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    container_name: orion-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes


volumes:
  postgres_data:
  redis_data:
services:
  orion:
    image: ghcr.io/mocha-fe/orion:latest
    ports:
      - "80:3000"
    volumes:
      - /etc/localtime:/etc/localtime:ro
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    container_name: orion-redis
    restart: always
    expose:
      - 6379
    volumes:
      - redis_data:/data
    command: redis-server --protected-mode no
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    networks:
      - app-network
  
  postgres:
    image: postgres:16-alpine
    container_name: orion-postgres
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: orion
    expose:
      - 5432
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  redis_data:

networks:
  app-network:
    driver: bridge

可以看到,生产环境和开发环境的 docker-compose.yaml 文件在结构和内容上基本是相同的,这种一致性使得我们能够保证整套系统的外部依赖相同,也能保证在部署到生产时尽可能的减少因为环境差异导致的部署失败问题。

当然,生产环境在配置细节上更注重安全性和稳定性,比如使用 expose 暴露 redis 和 postgresql 容器,而不是使用 ports,从而保证我们的 redis 与 postgresql 与外部网络的隔离

CI/CD

GitHub Actions 是一个持续集成和持续交付 (CI/CD) 平台,我们可以使用它来实现自动化构建、测试和部署工作。

在我们的项目的 .github/workflows 下你可以创建多个工作流配置文件,当代码推送到远程时,github 会自动执行它们,下面是目前在用的容器化工作流

name: Docker Image CI

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Login to GitHub Container Registry
        run: echo "${{ secrets.PACKAGE_WRITE }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

      - name: Build and push Docker image
        id: docker_build
        run: |
          IMAGE_NAME=ghcr.io/mocha-fe/orion:latest
          docker build . --tag $IMAGE_NAME
          docker push $IMAGE_NAME

pasted-image-1771766938877.webp

最后通过 ssh 命令进入到服务器更新 docker 镜像并重启即可

Powered By Hexo.js Hexo and Minima. Support By Oracle & Docker-Compose.