后端软件架构设计
1. 架构概览
后端服务基于 NestJS 框架构建,采用 模块化 (Modular) 架构。核心目标是实现高内聚、低耦合,并为未来的微服务拆分预留空间。
2. 模块划分 (Modules)
系统共划分为 10 个核心模块:
| 模块 | 职责 | 依赖服务 | 状态 |
|---|---|---|---|
| CoreModule | 全局基础设置(Config, Logger, Database, Redis) | PrismaService, Redis | 基础完成 |
| AuthModule | 用户登录、注册、JWT 签发与校验 | UserModule | 基础完成 |
| UserModule | 用户增删改查、个人信息管理 | PrismaService | 基础完成 |
| ProductModule | 产品管理、物模型 CRUD | PrismaService | 计划中 |
| DeviceModule | 设备生命周期管理、状态管理、设备影子 | PrismaService, Redis, MqttModule | 计划中 |
| MqttModule | MQTT 客户端封装、消息订阅发布、指令路由 | EMQX | 基础完成 |
| FirmwareModule | 固件上传、版本管理、OSS 存储代理 | PrismaService, StorageService | 计划中 |
| OtaModule | 升级任务创建调度、进度追踪、离线补发 | PrismaService, MqttModule, FirmwareModule | 计划中 |
| AlertModule | 告警规则引擎、通知渠道分发(小程序/邮件) | PrismaService, MqttModule | v0.5 待排期 |
| OrgModule | 组织/房间管理、设备归属分组 | PrismaService | v0.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)。
- 在线状态:
- 来源:设备属性上报驱动 + Redis TTL 120s。
- 机制:后端收到属性上报时写入 Redis
SET device:{id}:online 1 EX 120。若 120 秒内无新上报,Key 自动过期,设备判定离线。 - 查询:直接读 Redis Key 是否存在,速度极快。
- 属性状态 (影子) — 采用两级存储:
| 存储 | 角色 | 更新时机 |
|---|---|---|
| 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 内置异常:
BadRequestException、NotFoundException、ForbiddenException,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配置与安全。
