rwadurian/backend/services/admin-service/docs/ARCHITECTURE.md

25 KiB
Raw Permalink Blame History

Admin Service 架构文档

目录


1. 服务概述

1.1 服务职责

Admin Service 是 RWA Durian 项目的应用版本管理服务,负责:

  • 📱 版本发布管理: 管理 Android/iOS 应用版本的创建、更新、启用/禁用
  • 🔄 版本检查: 为移动端提供版本检查 API支持强制更新和普通更新
  • 📊 版本查询: 支持按平台、版本号、启用状态等条件查询版本信息
  • 🔐 SHA256 校验: 确保 APK/IPA 文件完整性和安全性

1.2 核心功能

功能 说明 API 端点
创建版本 发布新版本Android/iOS POST /api/v1/version
检查更新 移动端检查是否有新版本 GET /api/v1/version/check
查询版本 查询所有版本或特定版本 GET /api/v1/version
启用/禁用版本 控制版本可用性 PATCH /api/v1/version/:id/enable
PATCH /api/v1/version/:id/disable

2. 架构设计

2.1 架构模式

Admin Service 采用 DDD (领域驱动设计) + Hexagonal Architecture (六边形架构) 的混合架构模式。

┌─────────────────────────────────────────────────────────┐
│                    API Layer (NestJS)                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ Controllers  │  │   DTOs       │  │  Guards      │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│              Application Layer (Handlers)               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │   Commands   │  │   Queries    │  │   Events     │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│                    Domain Layer                         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │   Entities   │  │ Value Objects│  │  Services    │  │
│  │              │  │              │  │              │  │
│  │ AppVersion   │  │VersionCode   │  │VersionCheck │  │
│  │              │  │VersionName   │  │ Service      │  │
│  │              │  │ FileSize     │  │              │  │
│  │              │  │ FileSha256   │  │              │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
│                                                         │
│  ┌──────────────────────────────────────────────────┐  │
│  │          Repository Interfaces (Port)             │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│             Infrastructure Layer (Adapters)             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ Repositories │  │   Mappers    │  │   Prisma     │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
                      ┌──────────┐
                      │PostgreSQL│
                      └──────────┘

2.2 分层职责

API Layer (接口层)

  • Controllers: 处理 HTTP 请求,路由分发
  • DTOs: 定义请求/响应数据传输对象
  • Guards: 身份验证、权限控制 (暂未实现)
  • 依赖方向: → Application Layer

Application Layer (应用层)

  • Command Handlers: 处理写操作命令 (Create, Update, Delete)
  • Query Handlers: 处理读操作查询 (Get, List, Find)
  • Event Handlers: 处理领域事件 (暂未实现)
  • 依赖方向: → Domain Layer

Domain Layer (领域层)

  • Entities: 聚合根,包含业务逻辑 (AppVersion)
  • Value Objects: 不可变值对象 (VersionCode, VersionName, FileSize, FileSha256)
  • Domain Services: 跨实体的业务逻辑 (VersionCheckService)
  • Repository Interfaces: 持久化端口定义
  • 依赖方向: 无外部依赖 (核心层)

Infrastructure Layer (基础设施层)

  • Repositories: Repository 接口的 Prisma 实现
  • Mappers: 领域对象 ↔ 持久化对象转换
  • Prisma Client: 数据库 ORM
  • 依赖方向: → Domain Layer (依赖倒置)

3. 领域设计

3.1 领域模型

聚合根: AppVersion

class AppVersion {
  // 标识
  private readonly _id: string;
  private readonly _platform: Platform;

  // 版本信息
  private readonly _versionCode: VersionCode;
  private readonly _versionName: VersionName;
  private readonly _buildNumber: string;

  // 文件信息
  private _downloadUrl: string;
  private readonly _fileSize: FileSize;
  private readonly _fileSha256: FileSha256;

  // 更新信息
  private _changelog: string;
  private _isEnabled: boolean;
  private _isForceUpdate: boolean;

  // 审计信息
  private readonly _createdBy: string;
  private readonly _createdAt: Date;
  private _updatedBy: string;
  private _updatedAt: Date;
}

业务不变式:

  1. versionCode 必须是正整数
  2. versionName 必须符合语义化版本格式 (x.y.z)
  3. fileSize 必须大于 0
  4. fileSha256 必须是有效的 64 位十六进制字符串
  5. 同一平台同一版本号的版本只能有一个启用

值对象

VersionCode (版本号)

class VersionCode {
  constructor(private readonly value: number) {
    if (!Number.isInteger(value) || value < 1) {
      throw new DomainException('Version code must be a positive integer');
    }
  }

  isGreaterThan(other: VersionCode): boolean
  isLessThan(other: VersionCode): boolean
  equals(other: VersionCode): boolean
}

VersionName (版本名称)

class VersionName {
  private readonly SEMVER_REGEX = /^\d+\.\d+\.\d+$/;

  constructor(private readonly value: string) {
    if (!this.SEMVER_REGEX.test(value)) {
      throw new DomainException('Invalid semantic version format');
    }
  }

  get major(): number
  get minor(): number
  get patch(): number
}

FileSize (文件大小)

class FileSize {
  constructor(private readonly bytes: bigint) {
    if (bytes < 0n) {
      throw new DomainException('File size cannot be negative');
    }
  }

  toHumanReadable(): string // "1.50 MB"
  toMegabytes(): string     // "1.50"
}

FileSha256 (SHA256 哈希)

class FileSha256 {
  private readonly SHA256_REGEX = /^[a-f0-9]{64}$/;

  constructor(private readonly hash: string) {
    if (!this.SHA256_REGEX.test(hash.toLowerCase())) {
      throw new DomainException('Invalid SHA256 hash format');
    }
  }
}

3.2 领域服务

VersionCheckService

class VersionCheckService {
  async checkForUpdate(
    platform: Platform,
    currentVersionCode: number,
  ): Promise<VersionCheckResult> {
    // 1. 查找最新启用的版本
    const latestVersion = await this.repository.findLatestEnabledVersion(platform);

    // 2. 比较版本号
    if (!latestVersion || latestVersion.versionCode.value <= currentVersionCode) {
      return VersionCheckResult.noUpdate();
    }

    // 3. 返回更新信息
    return VersionCheckResult.hasUpdate({
      latestVersion: latestVersion.versionName.value,
      downloadUrl: latestVersion.downloadUrl,
      isForceUpdate: latestVersion.isForceUpdate,
      changelog: latestVersion.changelog,
    });
  }
}

3.3 业务规则

规则 实现位置 验证时机
版本号必须唯一 AppVersionRepository 创建版本时
禁用版本不能强制更新 AppVersion.disable() 禁用操作时
文件大小必须 > 0 FileSize VO 值对象创建时
SHA256 必须 64 位十六进制 FileSha256 VO 值对象创建时
版本名称必须符合 semver VersionName VO 值对象创建时

4. 技术栈

4.1 核心框架

技术 版本 用途
NestJS 10.0.0 Web 框架
TypeScript 5.1.3 编程语言
Node.js 20.x 运行时
Prisma 5.7.0 ORM
PostgreSQL 16 数据库

4.2 开发工具

工具 版本 用途
Jest 29.5.0 测试框架
ts-jest 29.1.0 TypeScript + Jest
Supertest 6.3.3 HTTP 测试
ESLint 8.42.0 代码检查
Prettier 3.0.0 代码格式化

4.3 部署工具

工具 用途
Docker 容器化
Docker Compose 多容器编排
Makefile 自动化脚本

5. 目录结构

admin-service/
├── src/
│   ├── api/                          # API 层
│   │   ├── controllers/              # 控制器
│   │   │   └── version.controller.ts
│   │   └── dtos/                     # 数据传输对象
│   │       ├── create-version.dto.ts
│   │       ├── update-version.dto.ts
│   │       ├── check-version.dto.ts
│   │       └── version-response.dto.ts
│   │
│   ├── application/                  # 应用层
│   │   ├── commands/                 # 命令
│   │   │   ├── create-version.command.ts
│   │   │   ├── enable-version.command.ts
│   │   │   └── disable-version.command.ts
│   │   ├── handlers/                 # 处理器
│   │   │   ├── create-version.handler.ts
│   │   │   ├── enable-version.handler.ts
│   │   │   └── disable-version.handler.ts
│   │   └── queries/                  # 查询
│   │       ├── find-version-by-id.query.ts
│   │       └── find-all-versions.query.ts
│   │
│   ├── domain/                       # 领域层
│   │   ├── entities/                 # 实体
│   │   │   └── app-version.entity.ts
│   │   ├── value-objects/            # 值对象
│   │   │   ├── version-code.vo.ts
│   │   │   ├── version-name.vo.ts
│   │   │   ├── file-size.vo.ts
│   │   │   └── file-sha256.vo.ts
│   │   ├── repositories/             # 仓储接口
│   │   │   └── app-version.repository.ts
│   │   ├── services/                 # 领域服务
│   │   │   └── version-check.service.ts
│   │   └── enums/                    # 枚举
│   │       └── platform.enum.ts
│   │
│   ├── infrastructure/               # 基础设施层
│   │   ├── persistence/              # 持久化
│   │   │   ├── repositories/         # 仓储实现
│   │   │   │   └── app-version.repository.impl.ts
│   │   │   └── mappers/              # 映射器
│   │   │       └── app-version.mapper.ts
│   │   └── prisma/                   # Prisma
│   │       └── prisma.service.ts
│   │
│   ├── shared/                       # 共享模块
│   │   ├── exceptions/               # 异常
│   │   │   ├── domain.exception.ts
│   │   │   └── application.exception.ts
│   │   └── utils/                    # 工具
│   │
│   ├── app.module.ts                 # 根模块
│   └── main.ts                       # 入口文件
│
├── prisma/
│   ├── schema.prisma                 # Prisma Schema
│   └── migrations/                   # 数据库迁移
│
├── test/                             # 测试
│   ├── unit/                         # 单元测试
│   ├── integration/                  # 集成测试
│   └── e2e/                          # E2E 测试
│
├── database/                         # 数据库初始化
│   ├── init.sql                      # 初始化脚本
│   └── README.md
│
├── docs/                             # 文档
│   ├── ARCHITECTURE.md               # 本文档
│   ├── API.md                        # API 文档
│   ├── DEVELOPMENT.md                # 开发指南
│   ├── TESTING.md                    # 测试文档
│   └── DEPLOYMENT.md                 # 部署文档
│
├── scripts/                          # 脚本
│   ├── test-in-wsl.sh
│   ├── run-wsl-tests.ps1
│   └── test-with-docker-db.sh
│
├── docker-compose.yml                # Docker Compose
├── Dockerfile                        # Dockerfile
├── Makefile                          # Make 命令
├── package.json                      # NPM 配置
├── tsconfig.json                     # TypeScript 配置
└── README.md                         # 项目说明

6. 数据流

6.1 创建版本流程

Client Request (POST /api/v1/version)
         │
         ▼
┌─────────────────────────────────────┐
│ VersionController.createVersion()   │ ← API Layer
└─────────────────────────────────────┘
         │ CreateVersionDto
         ▼
┌─────────────────────────────────────┐
│ CreateVersionHandler.execute()      │ ← Application Layer
│  1. Create Command                  │
│  2. Validate Business Rules         │
│  3. Call Repository                 │
└─────────────────────────────────────┘
         │ CreateVersionCommand
         ▼
┌─────────────────────────────────────┐
│ AppVersion.create()                 │ ← Domain Layer
│  1. Create Value Objects            │
│     - VersionCode                   │
│     - VersionName                   │
│     - FileSize                      │
│     - FileSha256                    │
│  2. Create Entity                   │
│  3. Apply Business Rules            │
└─────────────────────────────────────┘
         │ AppVersion Entity
         ▼
┌─────────────────────────────────────┐
│ AppVersionRepositoryImpl.save()     │ ← Infrastructure Layer
│  1. Map Entity → Prisma Model       │
│  2. Save to Database                │
│  3. Return Persisted Entity         │
└─────────────────────────────────────┘
         │
         ▼
    PostgreSQL

6.2 检查更新流程

Mobile Client (GET /api/v1/version/check?platform=android&versionCode=100)
         │
         ▼
┌─────────────────────────────────────┐
│ VersionController.checkForUpdate()  │ ← API Layer
└─────────────────────────────────────┘
         │ CheckVersionDto
         ▼
┌─────────────────────────────────────┐
│ VersionCheckService.checkForUpdate()│ ← Domain Service
│  1. Query Latest Enabled Version    │
│  2. Compare Version Codes           │
│  3. Build Update Result             │
└─────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────┐
│ AppVersionRepository                │ ← Repository
│  .findLatestEnabledVersion()        │
└─────────────────────────────────────┘
         │
         ▼
    PostgreSQL
         │
         ▼
┌─────────────────────────────────────┐
│ VersionCheckResult                  │ ← Response
│  - hasUpdate: boolean               │
│  - latestVersion: string            │
│  - downloadUrl: string              │
│  - isForceUpdate: boolean           │
│  - changelog: string                │
└─────────────────────────────────────┘

6.3 依赖方向

API Layer
    ↓ depends on
Application Layer
    ↓ depends on
Domain Layer (Core)
    ↑ implemented by
Infrastructure Layer

核心原则:

  • Domain Layer 不依赖任何外部层
  • Infrastructure Layer 通过接口依赖 Domain Layer (依赖倒置原则)
  • Application Layer 协调 Domain 和 Infrastructure
  • API Layer 仅依赖 Application Layer

7. 设计原则

7.1 SOLID 原则应用

原则 应用实例
S (单一职责) 每个值对象只负责一个验证逻辑
每个 Handler 只处理一个命令/查询
O (开闭原则) 新增平台类型无需修改现有代码
通过 enum 扩展实现
L (里氏替换) Repository 接口可被不同实现替换
Prisma, TypeORM, InMemory
I (接口隔离) Repository 接口仅定义必要方法
不强制实现不需要的功能
D (依赖倒置) Domain Layer 定义 Repository 接口
Infrastructure Layer 实现接口

7.2 DDD 战术模式

模式 应用
Entity AppVersion 聚合根
Value Object VersionCode, VersionName, FileSize, FileSha256
Aggregate AppVersion 作为聚合边界
Repository AppVersionRepository 接口及实现
Domain Service VersionCheckService 处理跨实体逻辑
Factory Method AppVersion.create() 静态工厂方法

8. 扩展性设计

8.1 新增平台支持

当需要支持新平台(如 HarmonyOS

  1. 枚举扩展 (domain/enums/platform.enum.ts):
export enum Platform {
  ANDROID = 'android',
  IOS = 'ios',
  HARMONYOS = 'harmonyos', // 新增
}
  1. 无需修改:
    • Entity 逻辑
    • Repository 实现
    • Controller/Handler

8.2 新增版本检查策略

当需要支持灰度发布、A/B 测试时:

  1. 新增领域服务:
class GrayReleaseService {
  async checkEligibility(userId: string, version: AppVersion): Promise<boolean>
}
  1. 修改 VersionCheckService:
async checkForUpdate(
  platform: Platform,
  currentVersionCode: number,
  userId?: string, // 新增参数
): Promise<VersionCheckResult>

9. 性能考量

9.1 数据库索引

-- 平台 + 版本号唯一索引
CREATE UNIQUE INDEX idx_platform_versioncode
ON "AppVersion" (platform, "versionCode");

-- 启用状态 + 平台 + 版本号索引(查询最新版本)
CREATE INDEX idx_enabled_platform_versioncode
ON "AppVersion" ("isEnabled", platform, "versionCode" DESC);

9.2 缓存策略

建议实现 (当前未实现):

@Injectable()
export class CachedVersionCheckService {
  constructor(
    private readonly versionCheckService: VersionCheckService,
    private readonly cacheManager: Cache,
  ) {}

  async checkForUpdate(platform: Platform, versionCode: number) {
    const cacheKey = `version:${platform}:${versionCode}`;
    const cached = await this.cacheManager.get(cacheKey);
    if (cached) return cached;

    const result = await this.versionCheckService.checkForUpdate(platform, versionCode);
    await this.cacheManager.set(cacheKey, result, { ttl: 300 }); // 5分钟
    return result;
  }
}

10. 安全性

10.1 文件校验

  • SHA256 验证: 确保下载文件未被篡改
  • 下载 URL: 建议使用 HTTPS + CDN
  • 文件大小: 防止异常大文件攻击

10.2 API 安全 (待实现)

@Controller('api/v1/version')
@UseGuards(JwtAuthGuard) // 管理端需要认证
export class VersionController {
  @Post()
  @UseGuards(RolesGuard)
  @Roles('admin', 'developer') // 仅管理员和开发者可创建版本
  async createVersion(@Body() dto: CreateVersionDto) {}

  @Get('check')
  // 公开端点,无需认证
  async checkForUpdate(@Query() dto: CheckVersionDto) {}
}

11. 监控和日志

11.1 关键指标

指标 说明 监控方式
版本检查 QPS 每秒查询次数 Prometheus + Grafana
创建版本成功率 创建操作成功/失败比例 Application Logs
数据库查询延迟 查询耗时 Prisma Metrics
强制更新触发率 强制更新用户占比 Business Metrics

11.2 日志记录

@Injectable()
export class CreateVersionHandler {
  private readonly logger = new Logger(CreateVersionHandler.name);

  async execute(command: CreateVersionCommand): Promise<AppVersion> {
    this.logger.log(`Creating version: ${command.platform} v${command.versionName}`);

    try {
      const version = await this.repository.save(appVersion);
      this.logger.log(`Version created successfully: ${version.id}`);
      return version;
    } catch (error) {
      this.logger.error(`Failed to create version: ${error.message}`, error.stack);
      throw error;
    }
  }
}

12. 未来改进

12.1 短期 (1-3 个月)

  • 实现 JWT 认证和 RBAC 权限控制
  • 添加版本删除功能(软删除)
  • 实现分页查询
  • 添加 Redis 缓存层

12.2 中期 (3-6 个月)

  • 实现灰度发布功能
  • 添加版本回滚机制
  • 实现版本发布审批流程
  • 集成 CDN 文件上传

12.3 长期 (6-12 个月)

  • 实现多渠道版本管理Google Play, App Store, 自建服务器)
  • 添加 A/B 测试支持
  • 实现版本使用统计和分析
  • 集成 Sentry 错误监控

最后更新: 2025-12-03 版本: 1.0.0 维护者: RWA Durian Team