Skip to content

后端软件架构设计

1. 架构概览

后端服务基于 NestJS 框架构建,采用 模块化 (Modular) 架构。核心目标是实现高内聚、低耦合,并为未来的微服务拆分预留空间。

2. 模块划分 (Modules)

系统共划分为 10 个核心模块:

模块职责依赖服务状态
CoreModule全局基础设置(Config, Logger, Database, Redis)PrismaService, Redis基础完成
AuthModule用户登录、注册、JWT 签发与校验UserModule基础完成
UserModule用户增删改查、个人信息管理PrismaService基础完成
ProductModule产品管理、物模型 CRUDPrismaService计划中
DeviceModule设备生命周期管理、状态管理、设备影子PrismaService, Redis, MqttModule计划中
MqttModuleMQTT 客户端封装、消息订阅发布、指令路由EMQX基础完成
FirmwareModule固件上传、版本管理、OSS 存储代理PrismaService, StorageService计划中
OtaModule升级任务创建调度、进度追踪、离线补发PrismaService, MqttModule, FirmwareModule计划中
AlertModule告警规则引擎、通知渠道分发(小程序/邮件)PrismaService, MqttModulev0.5 待排期
OrgModule组织/房间管理、设备归属分组PrismaServicev0.5 待排期

FirmwareModule 和 OtaModule 的详细设计见 05.固件与OTA服务,MQTT 消息路由的完整设计见 04.MQTT服务设计

3. 模块依赖关系

HTTP 请求

  ├─→ AuthModule ──→ UserModule

  ├─→ DeviceModule ──→ MqttModule.publish()(指令下发)
  │                 ├─→ WebSocket(状态推送)
  │                 └─→ Redis + PostgreSQL(影子更新)

  ├─→ ProductModule

  ├─→ OrgModule ──→ DeviceModule(设备归属分组)

  └─→ OtaModule ──→ FirmwareModule(查询固件 URL)
                 ├─→ MqttModule.publish()(升级通知下发)
                 └─→ WebSocket(进度推送)

MQTT 消息(设备上报)

  └─→ MqttModule(路由分发)
        ├─→ DeviceModule(属性上报、set_reply)
        ├─→ OtaModule(OTA 进度)
        └─→ AlertModule(告警事件,v0.5)

各模块间通过 NestJS 的依赖注入互相调用,不通过 HTTP 内部请求(避免网络开销)。

4. 核心业务流程实现

4.1 消息路由策略

MQTT 消息通过装饰器式路由分发到各业务模块,完整设计(装饰器定义、注册流程、路由表、Context 解析)见 04.MQTT服务设计

4.2 设备状态管理 (DeviceService)

设备状态分为 实时状态 (Online/Offline)属性状态 (Shadow)

  1. 在线状态
    • 来源:设备属性上报驱动 + Redis TTL 120s。
    • 机制:后端收到属性上报时写入 Redis SET device:{id}:online 1 EX 120。若 120 秒内无新上报,Key 自动过期,设备判定离线。
    • 查询:直接读 Redis Key 是否存在,速度极快。
  2. 属性状态 (影子) — 采用两级存储:
存储角色更新时机
Redis (Hash)热缓存,WebSocket 推送零延迟读取MQTT 消息到达后最先写入
PostgreSQL (JSONB)权威数据,API 查询和审计Redis 写入后同步写入
InfluxDB时序历史,趋势分析和图表异步写入,不阻塞推送路径

同步策略:MQTT 消息到达 → 写 Redis Hash → 写 PostgreSQL 影子 → 异步写 InfluxDB。Redis 不可用时降级读 PostgreSQL。

设计原因和完整同步策略见 12.缓存策略 第 3 节。

5. 目录结构规范

bash
src/
├── common/             # 通用组件 (过滤器, 守卫, 装饰器)
   ├── decorators/
   ├── filters/
   └── guards/
├── config/             # 环境变量配置
├── modules/            # 业务模块
   ├── auth/
   ├── user/
   ├── product/
   ├── device/
   ├── mqtt/           # MQTT 封装与消息路由
   ├── organization/   # 组织与房间管理
   ├── firmware/       # 固件管理
   └── ota/            # OTA 升级
├── shared/             # 共享服务 (Redis, Util)
└── main.ts             # 入口文件

6. 请求生命周期

一个 API 请求经过的各层及职责:

HTTP 请求 → Nginx → NestJS

  ├─→ HelmetMiddleware     ← HTTP 安全头
  ├─→ CorsMiddleware       ← 跨域
  ├─→ ThrottlerGuard       ← 限流(60s/60次)
  ├─→ AuthGuard            ← JWT 验证(公开接口跳过)
  ├─→ RolesGuard           ← 角色权限校验
  ├─→ ValidationPipe       ← 参数校验(DTO whitelist + transform)

  ├─→ Controller           ← 路由匹配,调用 Service
  │     └─→ Service        ← 业务逻辑
  │           └─→ Repository (Prisma) ← 数据库操作

  ├─→ TransformInterceptor ← 响应格式统一包装为 { code, msg, data }
  └─→ HttpExceptionFilter  ← 异常统一处理,映射为业务错误码

错误码体系见 08.API接口规范 错误码章节。

7. 异常处理策略

  • 所有业务异常通过 HttpExceptionFilter 统一捕获
  • Service 层抛 NestJS 内置异常:BadRequestExceptionNotFoundExceptionForbiddenException,Filter 自动映射为对应的业务错误码
  • 未预期异常(500)记录完整堆栈到 Winston 日志,前端只显示通用错误提示
  • MQTT 消息处理异常不回发错误(见 04.MQTT服务设计 第 3 节)

8. 鉴权与安全

  • API 鉴权:使用 Passport + JWT 策略。所有 /api/* 接口(除登录注册)均需携带 Bearer Token。
  • 设备鉴权
    • 设备连接 EMQX 时,使用 ClientId (MAC 地址) + Username (ProductKey) + Password (DeviceSecret) 认证。
    • EMQX 通过 PostgreSQL 认证插件直连数据库,查询 device 表验证 ProductKey 和 DeviceSecret 是否匹配。设备需预先在平台注册(数据库中已存在记录),否则认证失败。

EMQX 侧的具体配置见 09.EMQX配置与安全