From a979338ba820d05fa748ad365e09f132a83be3c1 Mon Sep 17 00:00:00 2001 From: StephenQiu30 <89136224+StephenQiu30@users.noreply.github.com> Date: Tue, 16 Jun 2026 21:44:38 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20PRD08=20=E5=90=8E=E7=AB=AF=E5=B7=A5?= =?UTF-8?q?=E7=A8=8B=E8=A7=84=E8=8C=83=E4=B8=8E=E6=9E=B6=E6=9E=84=E6=B2=BB?= =?UTF-8?q?=E7=90=86=20=E2=80=94=20OpenSpec=20change,=20PRD,=20PLAN13,=20R?= =?UTF-8?q?EADME,=20validate-repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 docs/prd/08-后端工程规范与架构治理.md:分层架构、SOLID 边界、shared 真相源、契约优先、测试优先治理 - 创建 docs/plans/13-后端工程规范与架构治理计划.md:5 阶段渐进治理任务 - 创建 openspec/changes/backend-engineering-governance/:proposal, design, specs, tasks - 更新 docs/prd/README.md 和 docs/plans/README.md 添加文档列表 - 更新 scripts/validate-repository.sh 添加 PRD08 和 PLAN13 文件检查 --- ...73\347\220\206\350\256\241\345\210\222.md" | 124 +++++++++++++ docs/plans/README.md | 16 ++ ...66\346\236\204\346\262\273\347\220\206.md" | 166 ++++++++++++++++++ docs/prd/README.md | 11 ++ .../backend-engineering-governance/design.md | 94 ++++++++++ .../proposal.md | 32 ++++ .../specs/architecture-boundaries/spec.md | 79 +++++++++ .../specs/shared-constants/spec.md | 46 +++++ .../backend-engineering-governance/tasks.md | 56 ++++++ scripts/validate-repository.sh | 2 + 10 files changed, 626 insertions(+) create mode 100644 "docs/plans/13-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206\350\256\241\345\210\222.md" create mode 100644 "docs/prd/08-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206.md" create mode 100644 openspec/changes/backend-engineering-governance/design.md create mode 100644 openspec/changes/backend-engineering-governance/proposal.md create mode 100644 openspec/changes/backend-engineering-governance/specs/architecture-boundaries/spec.md create mode 100644 openspec/changes/backend-engineering-governance/specs/shared-constants/spec.md create mode 100644 openspec/changes/backend-engineering-governance/tasks.md diff --git "a/docs/plans/13-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206\350\256\241\345\210\222.md" "b/docs/plans/13-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206\350\256\241\345\210\222.md" new file mode 100644 index 0000000..9160c7e --- /dev/null +++ "b/docs/plans/13-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206\350\256\241\345\210\222.md" @@ -0,0 +1,124 @@ +--- +layer: Plan +doc_no: "13" +audience: + - Dev + - QA +feature_area: backend-engineering-governance +purpose: "落地 PRD08 的后端工程治理要求:分层架构、shared 真相源、架构边界测试和复杂度控制。" +canonical_path: "docs/plans/13-后端工程规范与架构治理计划.md" +status: draft +version: "1.0.0" +owner: "StephenQiu30" +inputs: + - "docs/prd/08-后端工程规范与架构治理.md" + - "docs/design/01-个人自部署万能视频下载器技术设计.md" +outputs: + - "后端工程治理实施计划" +triggers: + - "新增或重构后端模块" + - "调整 shared 常量或错误码" +downstream: + - "后续涉及后端治理的实现计划" +--- + +# PLAN13 后端工程规范与架构治理 + +## 1. 背景 + +PRD08 定义了后端分层架构、SOLID 边界、shared 真相源和测试优先治理要求。PLAN13 将这些要求转化为可执行的渐进治理任务,不追求一次性重排全部代码,而是通过每次真实变更逐步收敛。 + +## 2. 目标 + +1. 将 PRD08 的分层架构定义落到可验证的目录结构和依赖约束。 +2. 建立 shared 真相源,将分散的错误码和常量迁移到 `packages/shared/`。 +3. 建立架构边界测试,防止跨层依赖引入。 +4. 建立复杂度控制检查,防止单文件/单函数过度膨胀。 + +## 3. 非目标 + +- 不一次性重写全部已有模块。 +- 不引入 CI 自动化架构扫描工具(如 import-linter),留待后续评估。 +- 不覆盖 Worker 层的独立 repository 抽取(由后续 PLAN 处理)。 + +## 4. 核心内容 + +### 4.1 Phase 1:Shared 真相源建立 + +| 任务 | 文件 | 验证命令 | +| --- | --- | --- | +| 在 `packages/shared/` 创建 `codes.py`,定义所有 `failure_code` 常量 | `packages/shared/video_downloader_shared/codes.py` | `python -c "from video_downloader_shared.codes import *"` | +| 在 `packages/shared/` 创建 `platforms.py`,定义平台 ID 常量 | `packages/shared/video_downloader_shared/platforms.py` | `python -c "from video_downloader_shared.platforms import *"` | +| 在 `packages/shared/` 创建 `dto.py`,定义跨模块 DTO | `packages/shared/video_downloader_shared/dto.py` | `python -c "from video_downloader_shared.dto import *"` | +| 更新 `packages/shared/video_downloader_shared/__init__.py` 导出新模块 | `packages/shared/video_downloader_shared/__init__.py` | `python -c "import video_downloader_shared"` | + +### 4.2 Phase 2:分层目录骨架 + +| 任务 | 文件 | 验证命令 | +| --- | --- | --- | +| 创建 `apps/api/app/domain/` 目录和 `__init__.py` | `apps/api/app/domain/__init__.py` | `python -c "from app.domain import *"` | +| 创建 `apps/api/app/repositories/` 目录和 `__init__.py` | `apps/api/app/repositories/__init__.py` | `python -c "from app.repositories import *"` | +| 创建 `apps/api/app/infrastructure/` 目录和 `__init__.py` | `apps/api/app/infrastructure/__init__.py` | `python -c "from app.infrastructure import *"` | +| 在各 `__init__.py` 中写入层级职责说明注释 | 各 `__init__.py` | 人工审阅 | + +### 4.3 Phase 3:架构边界测试 + +| 任务 | 文件 | 验证命令 | +| --- | --- | --- | +| 编写架构边界测试:验证 router 不直接引用 ORM | `apps/api/tests/test_architecture_boundaries.py` | `npm test -- test_architecture_boundaries` | +| 编写架构边界测试:验证 domain 不引用基础设施 | `apps/api/tests/test_architecture_boundaries.py` | `npm test -- test_architecture_boundaries` | +| 编写架构边界测试:验证 repository 不含业务逻辑 | `apps/api/tests/test_architecture_boundaries.py` | `npm test -- test_architecture_boundaries` | +| 编写 shared 常量完整性测试:验证无硬编码错误码 | `apps/api/tests/test_shared_constants.py` | `npm test -- test_shared_constants` | + +### 4.4 Phase 4:复杂度控制 + +| 任务 | 文件 | 验证命令 | +| --- | --- | --- | +| 编写文件行数检查脚本 | `scripts/check_complexity.sh` | `bash scripts/check_complexity.sh` | +| 将复杂度检查集成到 `npm test` | `package.json` | `npm test` | +| 对超限文件标注拆分 TODO | 各超限文件 | 人工审阅 | + +### 4.5 Phase 5:文档与 OpenSpec 同步 + +| 任务 | 文件 | 验证命令 | +| --- | --- | --- | +| 将 architecture-boundaries spec 推广到 `openspec/specs/` | `openspec/specs/architecture-boundaries/spec.md` | 人工审阅 | +| 将 shared-constants spec 推广到 `openspec/specs/` | `openspec/specs/shared-constants/spec.md` | 人工审阅 | +| 更新 `docs/prd/README.md` 添加 PRD08 | `docs/prd/README.md` | 人工审阅 | +| 更新 `docs/plans/README.md` 添加 PLAN13 | `docs/plans/README.md` | 人工审阅 | +| 更新 `scripts/validate-repository.sh` 添加新文件检查 | `scripts/validate-repository.sh` | `bash scripts/validate-repository.sh` | + +## 5. 关联文档 + +### 5.1 输入文档 + +1. `docs/prd/08-后端工程规范与架构治理.md` — 分层架构、shared 真相源、测试治理要求 +2. `docs/design/01-个人自部署万能视频下载器技术设计.md` — §4.3 目录与模块边界 + +### 5.2 输出文档 + +1. `openspec/specs/architecture-boundaries/spec.md` +2. `openspec/specs/shared-constants/spec.md` + +## 6. 验收门禁 + +- `packages/shared/video_downloader_shared/codes.py` 存在并可导入。 +- `apps/api/app/domain/`、`apps/api/app/repositories/`、`apps/api/app/infrastructure/` 目录存在。 +- 架构边界测试通过。 +- `bash scripts/check_complexity.sh` 通过。 +- `bash scripts/validate-repository.sh` 通过。 + +## 7. 风险与边界 + +渐进治理意味着短期内新旧代码风格并存。Phase 1-2 建立骨架和真相源,Phase 3-4 建立防护,后续每次真实变更时逐步将已有代码迁移到治理结构中。不要求一次性迁移所有已有模块。 + +## 8. 待确认问题 + +- Worker 层是否需要独立的 repository 抽取,还是复用 API 层 repository。 +- 是否需要在 CI 中添加 `scripts/check_complexity.sh` 作为必过检查。 + +## 9. 变更记录 + +| 日期 | 作者 | 版本 | 变更说明 | +| --- | --- | --- | --- | +| 2026-06-16 | StephenQiu30 | 1.0.0 | 新建 PLAN13:后端工程规范与架构治理 | diff --git a/docs/plans/README.md b/docs/plans/README.md index 325e276..6ac5152 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -2,6 +2,22 @@ 本目录存放执行计划类文档。 +## 文档列表 + +| 编号 | 标题 | 状态 | +| --- | --- | --- | +| PLAN01 | URL 协议与地址安全计划 | accepted | +| PLAN02 | 错误语义与日志脱敏计划 | accepted | +| PLAN03 | 平台画像注册与识别计划 | accepted | +| PLAN04 | 创建任务与状态查询计划 | accepted | +| PLAN05 | Worker 下载执行与失败分类计划 | accepted | +| PLAN06 | 取消重试与事件流计划 | accepted | +| PLAN07 | MinIO 对象归档计划 | accepted | +| PLAN08 | 预签名下载与过期清理计划 | accepted | +| PLAN09 | 本机开发与依赖复用计划 | accepted | +| PLAN10 | DockerCompose 部署与健康检查计划 | accepted | +| PLAN13 | 后端工程规范与架构治理计划 | draft | + ## 适合放入 1. 阶段计划与任务拆解。 diff --git "a/docs/prd/08-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206.md" "b/docs/prd/08-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206.md" new file mode 100644 index 0000000..71a389d --- /dev/null +++ "b/docs/prd/08-\345\220\216\347\253\257\345\267\245\347\250\213\350\247\204\350\214\203\344\270\216\346\236\266\346\236\204\346\262\273\347\220\206.md" @@ -0,0 +1,166 @@ +--- +layer: PRD +doc_no: "08" +audience: + - PM + - Dev + - QA +feature_area: backend-engineering-governance +purpose: "定义 video-server 后端分层架构、SOLID 边界、shared 真相源、契约优先和测试优先治理要求。" +canonical_path: "docs/prd/08-后端工程规范与架构治理.md" +status: draft +version: "1.0.0" +owner: "StephenQiu30" +inputs: + - "CLAUDE.md" + - "docs/design/01-个人自部署万能视频下载器技术设计.md" +outputs: + - "后端工程治理边界" + - "架构分层验收标准" +triggers: + - "新增或重构后端模块" + - "调整 router / schema / service / repository / worker 职责" + - "引入新的 shared 常量或错误码" +downstream: + - "docs/plans/13-后端工程规范与架构治理计划.md" +--- + +# PRD08 后端工程规范与架构治理 + +## 1. 背景 + +video-server 当前代码结构基本遵循 `routers → services → models` 的三层模式,但缺少正式的架构治理规范。随着功能增长,以下风险逐渐暴露: + +1. `services/` 层同时承担业务编排和数据库读写,职责边界模糊。 +2. 错误码、状态枚举和常量分散在各模块,缺少 shared 真相源约束。 +3. 新增模块缺乏明确的层级归属和依赖方向约束,容易出现跨层依赖。 +4. 重构和扩展缺少测试门禁保护,容易引入回归。 + +PRD08 为后续改造建立治理边界,使每次变更都可追溯到层级、依赖方向和测试门禁。 + +## 2. 目标 + +1. 定义 router、schema、service、domain、repository、infrastructure、worker job 的职责和禁止事项。 +2. 确立 shared 包(`packages/shared/`)作为状态枚举、错误码和跨模块常量的唯一真相源。 +3. 要求新增或重构模块必须说明所属层级、依赖方向和测试门禁。 +4. 以渐进治理为原则:只约束真实变更和扩展点,不要求一次性重排全部代码。 + +```gherkin +Given 开发者需要新增或重构一个后端模块 +When 查阅 PRD08 +Then 能明确该模块所属层级、允许依赖方向、必须遵循的契约和测试要求 +``` + +## 3. 非目标 + +- 不要求一次性重写全部目录结构。 +- 不引入大型治理平台或自动化扫描工具链。 +- 不覆盖前端工程规范。 +- 不为了形式抽象而引入无真实调用者的接口层。 + +## 4. 核心内容 + +### 4.1 分层架构定义 + +| 层级 | 路径 | 职责 | 禁止事项 | +| --- | --- | --- | --- | +| Router | `apps/api/app/routers/` | 协议层:接收请求、调用 service、返回响应 | 不含业务逻辑、不含数据库操作 | +| Schema | `apps/api/app/schemas.py` | Pydantic 契约:请求/响应模型定义 | 不含业务逻辑、不直接引用 ORM 模型 | +| Service | `apps/api/app/services/` | 业务编排:协调 domain、repository 和 infrastructure | 不直接写 SQL、不直接调用外部 API | +| Domain | `apps/api/app/domain/` | 纯业务规则:状态机、校验、计算 | 不引用数据库、不引用外部服务 | +| Repository | `apps/api/app/repositories/` | 数据库读写:封装 SQLAlchemy 操作 | 不含业务逻辑、只做 CRUD 和查询 | +| Infrastructure | `apps/api/app/infrastructure/` | 外部服务封装:Redis、MinIO、队列 | 不含业务逻辑、只做适配 | +| Worker Job | `apps/worker/worker/jobs/` | 异步任务入口:从队列拾取任务并编排执行 | 不直接操作数据库、通过 repository 更新状态 | + +### 4.2 依赖方向约束 + +```text +Router → Service → Domain + → Repository → ORM/DB + → Infrastructure → 外部服务 +Worker Job → Service → Domain + → Repository → ORM/DB + → Infrastructure → 外部服务 +``` + +**规则**: + +1. 上层可以依赖下层,下层不得依赖上层。 +2. 同层之间避免循环依赖。 +3. Domain 层是纯函数,不引用任何基础设施。 +4. Repository 和 Infrastructure 不互相依赖。 + +### 4.3 Shared 真相源 + +`packages/shared/video_downloader_shared/` 是跨模块常量和枚举的唯一来源: + +| 内容 | 当前位置 | 目标位置 | +| --- | --- | --- | +| `TaskState` 枚举 | `packages/shared/video_downloader_shared/states.py` | 保持不变 | +| `failure_code` 常量 | 分散在 services 和 worker | 迁移到 `shared/codes.py` | +| 平台 ID 常量 | 分散在 services 和 worker | 迁移到 `shared/platforms.py` | +| 跨模块 DTO | 各模块自定义 | 迁移到 `shared/dto.py` | + +**规则**: + +1. 新增错误码必须先在 `shared/codes.py` 定义,再在业务代码中引用。 +2. 新增状态枚举必须先在 `shared/states.py` 定义。 +3. 禁止在业务模块中硬编码错误码字符串或状态值。 + +### 4.4 契约优先 + +1. API 请求/响应模型通过 Pydantic schema 定义,schema 变更先于实现。 +2. OpenAPI spec 通过 `scripts/export_openapi.py` 导出,作为前端集成的契约。 +3. 新增或修改 API 端点时,必须同步更新 schema 和 OpenAPI 导出。 + +### 4.5 测试优先治理 + +1. 新增模块必须有对应测试,测试覆盖核心业务规则和边界条件。 +2. 重构已有模块时,必须先有保护测试,再做结构变更。 +3. 测试不得为兼容旧行为、灰度双轨或临时兜底补写兼容性测试。 +4. 架构边界测试:验证依赖方向约束,防止跨层引用。 + +### 4.6 复杂度控制 + +1. 单文件长期目标不超过 200 行;超过时按职责拆分。 +2. 单函数不超过 50 行;超过时提取子函数。 +3. 圈复杂度不超过 10;超过时重构条件分支。 +4. 新增模块必须在 PR 中说明所属层级和依赖方向。 + +## 5. 关联文档 + +### 5.1 输入文档 + +1. `CLAUDE.md` — 提交规范、SDD/TDD/RAG 门禁、交付格式 +2. `docs/design/01-个人自部署万能视频下载器技术设计.md` — §4.3 目录与模块边界 + +### 5.2 输出文档 + +1. `docs/plans/13-后端工程规范与架构治理计划.md` + +### 5.3 下游文档 + +1. 后续所有涉及后端新增或重构的 PLAN 文档 + +## 6. 验收门禁 + +- PRD08 路径存在并能独立说明后端工程治理边界。 +- 分层架构表格完整,每层有职责和禁止事项。 +- 依赖方向约束清晰,无歧义。 +- Shared 真相源规则明确。 +- 治理要求不要求一次性大规模重排所有代码。 + +## 7. 风险与边界 + +过度治理会拖慢迭代速度。PRD08 的原则是"治理只服务真实变更和扩展点"——如果一个模块没有被改动,不要求为它补充治理。渐进治理意味着短期内会存在新旧代码风格并存的情况,但每次变更都向治理目标收敛。 + +## 8. 待确认问题 + +- 是否需要为 Worker 层引入独立的 `worker/repositories/` 还是复用 API 层的 repository。 +- 是否需要在 CI 中添加自动化的架构边界检查(如 import-linter)。 + +## 9. 变更记录 + +| 日期 | 作者 | 版本 | 变更说明 | +| --- | --- | --- | --- | +| 2026-06-16 | StephenQiu30 | 1.0.0 | 新建 PRD08:后端工程规范与架构治理 | diff --git a/docs/prd/README.md b/docs/prd/README.md index 3cbb5c5..970c91c 100644 --- a/docs/prd/README.md +++ b/docs/prd/README.md @@ -22,6 +22,17 @@ PRD 的核心目的不是堆叠描述,而是让需求更清楚,让代码更 使用 `顺序编号-语义化标题.md`,例如 `01-解析入口与URL安全.md`。 +## 文档列表 + +| 编号 | 标题 | 状态 | +| --- | --- | --- | +| PRD01 | 解析入口与 URL 安全 | draft | +| PRD02 | 平台识别与平台画像 | draft | +| PRD03 | 异步下载任务主链路 | draft | +| PRD04 | MinIO 产物归档与下载交付 | draft | +| PRD05 | 自部署运行与环境复用 | draft | +| PRD08 | 后端工程规范与架构治理 | draft | + ## 推荐结构 PRD 应优先使用 `docs/TEMPLATE.md` 的 frontmatter,并按以下结构扩展: diff --git a/openspec/changes/backend-engineering-governance/design.md b/openspec/changes/backend-engineering-governance/design.md new file mode 100644 index 0000000..ff3db88 --- /dev/null +++ b/openspec/changes/backend-engineering-governance/design.md @@ -0,0 +1,94 @@ +## Context + +video-server 当前目录结构: + +```text +apps/api/app/ + routers/ — 协议层 + schemas.py — Pydantic 契约 + services/ — 业务编排 + 数据库读写(职责混合) + models.py — SQLAlchemy ORM 模型 + core/ — 配置、错误处理、日志、安全 + db/ — 数据库会话和迁移 + middleware/ — 请求上下文 + utils/ — 工具函数 + +apps/worker/worker/ + jobs/ — 异步任务入口 + domain.py — 业务规则 + download_runner.py — 下载执行 + ... + +packages/shared/video_downloader_shared/ + states.py — TaskState 枚举 +``` + +设计文档 `docs/design/01-个人自部署万能视频下载器技术设计.md` §4.3 已定义目标分层(routers/schemas/services/repositories/worker-jobs/worker-adapters/shared),但当前实现缺少 `domain/`、`repositories/`、`infrastructure/` 目录,`services/` 层混合了业务编排和数据库操作。 + +### 现有问题 + +1. `services/tasks.py` 直接操作 SQLAlchemy session,违反 service 层只做业务编排的原则。 +2. 错误码(`failure_code`)以字符串字面量分散在 `services/` 和 `worker/` 中,无统一定义。 +3. 平台 ID 常量分散在 `services/platforms.py` 和 `worker/` 中。 +4. 无架构边界测试,跨层依赖只能通过人工审阅发现。 + +## Goals / Non-Goals + +**Goals:** + +- 建立 domain、repository、infrastructure 分层目录骨架 +- 将错误码、平台 ID 和跨模块 DTO 迁移到 `packages/shared/` +- 建立架构边界测试,自动验证依赖方向约束 +- 建立复杂度控制检查脚本 +- 渐进治理:不要求一次性迁移所有已有代码 + +**Non-Goals:** + +- 不一次性重写 `services/` 层的所有数据库操作到 repository +- 不引入 CI 自动化架构扫描工具(import-linter 等) +- 不为 Worker 层创建独立的 repository(留待后续 PLAN) +- 不修改任何已有功能的行为 + +## Decisions + +### 1. Domain 层为纯函数目录 + +**选择**:`apps/api/app/domain/` 存放纯业务规则函数,不引用数据库或外部服务。 + +**理由**:Domain 层是业务逻辑的核心,纯函数易于测试和复用。当前 `services/` 中的校验逻辑(如 URL 校验、并发检查)可逐步迁移到 domain。 + +### 2. Repository 层封装 SQLAlchemy 操作 + +**选择**:`apps/api/app/repositories/` 存放数据库读写函数,每个函数对应一个原子操作。 + +**理由**:将数据库操作从 service 层分离,使 service 只做业务编排。Repository 函数接收 session 作为参数,不自行创建会话。 + +### 3. Infrastructure 层封装外部服务 + +**选择**:`apps/api/app/infrastructure/` 存放 Redis、MinIO、队列等外部服务的适配函数。 + +**理由**:将外部服务调用从 service 层分离,便于 mock 测试和替换实现。 + +### 4. Shared 包使用模块文件而非子包 + +**选择**:新增 `codes.py`、`platforms.py`、`dto.py` 作为 `packages/shared/video_downloader_shared/` 下的模块文件。 + +**理由**:当前 `states.py` 已经是单文件模式,保持一致。跨模块常量规模不大,不需要子包拆分。 + +### 5. 架构边界测试通过 import 分析实现 + +**选择**:`test_architecture_boundaries.py` 通过解析 Python 源码的 import 语句验证依赖方向。 + +**理由**:不需要引入额外依赖(如 import-linter),测试本身即文档。通过 AST 解析 import 比正则匹配更可靠。 + +### 6. 复杂度检查脚本使用简单行数统计 + +**选择**:`scripts/check_complexity.sh` 检查 Python 文件行数,超过 200 行的文件报 warning。 + +**理由**:行数是最简单可靠的复杂度代理指标。圈复杂度检查需要额外工具(如 radon),留待后续评估。 + +## Risks / Trade-offs + +- **风险**:渐进治理导致新旧代码风格并存 → **缓解**:架构边界测试确保新代码遵守约束,旧代码在被改动时逐步迁移 +- **风险**:domain 层初始为空,可能被质疑过度设计 → **缓解**:从 `services/` 中提取第一个纯业务函数时才创建 domain 模块 +- **风险**:shared 常量迁移可能引入循环依赖 → **缓解**:shared 包不依赖任何 app 代码,只被 app 代码单向依赖 diff --git a/openspec/changes/backend-engineering-governance/proposal.md b/openspec/changes/backend-engineering-governance/proposal.md new file mode 100644 index 0000000..25e4398 --- /dev/null +++ b/openspec/changes/backend-engineering-governance/proposal.md @@ -0,0 +1,32 @@ +## Why + +video-server 后端代码结构基本遵循 `routers → services → models` 三层模式,但缺少正式的架构治理规范。`services/` 层同时承担业务编排和数据库读写,错误码和常量分散在各模块,新增模块缺乏明确的层级归属和依赖方向约束。PRD08 已定义治理边界,需要通过 OpenSpec 将分层架构、shared 真相源和架构边界测试规范化,为后续改造提供可验证的治理基线。 + +## What Changes + +- 新增 OpenSpec spec `architecture-boundaries`:定义 router/schema/service/domain/repository/infrastructure/worker-job 的职责边界和依赖方向约束 +- 新增 OpenSpec spec `shared-constants`:定义 `packages/shared/` 作为错误码、状态枚举和跨模块常量的唯一真相源 +- 创建 `apps/api/app/domain/`、`apps/api/app/repositories/`、`apps/api/app/infrastructure/` 分层目录骨架 +- 创建 `packages/shared/video_downloader_shared/codes.py`、`platforms.py`、`dto.py` 真相源模块 +- 创建架构边界测试和 shared 常量完整性测试 +- 创建复杂度控制检查脚本 +- 将 specs 推广到 `openspec/specs/` 作为当前事实层 +- 更新 `scripts/validate-repository.sh` 添加新文件检查 + +## Capabilities + +### New Capabilities + +- `architecture-boundaries`: 后端分层架构职责边界和依赖方向约束规范 +- `shared-constants`: 跨模块常量和枚举作为唯一真相源的规范 + +### Modified Capabilities + +(无已有 spec 需要修改) + +## Impact + +- 受影响代码:`apps/api/app/`(新增 domain/repositories/infrastructure 目录)、`packages/shared/video_downloader_shared/`(新增 codes/platforms/dto 模块) +- 受影响测试:`apps/api/tests/test_architecture_boundaries.py`、`apps/api/tests/test_shared_constants.py` +- 受影响文档:`docs/prd/08-后端工程规范与架构治理.md`、`docs/plans/13-后端工程规范与架构治理计划.md`、`docs/prd/README.md`、`docs/plans/README.md` +- 受影响脚本:`scripts/validate-repository.sh`、`scripts/check_complexity.sh` diff --git a/openspec/changes/backend-engineering-governance/specs/architecture-boundaries/spec.md b/openspec/changes/backend-engineering-governance/specs/architecture-boundaries/spec.md new file mode 100644 index 0000000..af39a29 --- /dev/null +++ b/openspec/changes/backend-engineering-governance/specs/architecture-boundaries/spec.md @@ -0,0 +1,79 @@ +# Architecture Boundaries + +## Purpose + +定义 video-server 后端各层的职责边界、依赖方向约束和禁止事项。 + +## ADDED Requirements + +### Requirement: Router layer only handles protocol +Router 层 SHALL 只负责接收请求、调用 service 层和返回响应,不得包含业务逻辑或数据库操作。 + +#### Scenario: Router delegates to service +- **WHEN** API 接收请求 +- **THEN** Router 只做参数解析、认证校验和响应序列化,业务逻辑由 service 层处理 + +#### Scenario: Router does not import ORM models +- **WHEN** 检查 `apps/api/app/routers/` 的 import 语句 +- **THEN** 不得直接导入 `apps/api/app/models.py` 中的 ORM 模型 + +### Requirement: Schema layer only defines contracts +Schema 层 SHALL 只定义 Pydantic 请求/响应模型,不得包含业务逻辑或直接引用 ORM 模型。 + +#### Scenario: Schema uses Pydantic BaseModel +- **WHEN** 定义请求或响应模型 +- **THEN** 使用 Pydantic `BaseModel`,不继承 SQLAlchemy 模型 + +### Requirement: Service layer only orchestrates +Service 层 SHALL 只做业务编排:协调 domain、repository 和 infrastructure,不得直接写 SQL 或直接调用外部 API。 + +#### Scenario: Service delegates DB operations to repository +- **WHEN** Service 需要读写数据库 +- **THEN** 调用 repository 层函数,不直接操作 SQLAlchemy session + +#### Scenario: Service delegates external calls to infrastructure +- **WHEN** Service 需要调用 Redis、MinIO 或队列 +- **THEN** 调用 infrastructure 层函数,不直接使用外部客户端 + +### Requirement: Domain layer is pure business logic +Domain 层 SHALL 只包含纯业务规则:状态机、校验、计算。不得引用数据库、外部服务或框架组件。 + +#### Scenario: Domain function has no side effects +- **WHEN** 调用 domain 层函数 +- **THEN** 函数不访问数据库、不调用外部服务、不修改全局状态 + +### Requirement: Repository layer encapsulates DB operations +Repository 层 SHALL 封装所有 SQLAlchemy 数据库操作,每个函数对应一个原子 CRUD 或查询操作。 + +#### Scenario: Repository receives session as parameter +- **WHEN** 调用 repository 函数 +- **THEN** 函数接收 `db: Session` 作为参数,不自行创建会话 + +#### Scenario: Repository does not contain business logic +- **WHEN** 检查 repository 函数 +- **THEN** 函数只做数据存取,不包含条件分支、状态转换或业务规则 + +### Requirement: Infrastructure layer encapsulates external services +Infrastructure 层 SHALL 封装 Redis、MinIO、队列等外部服务的调用。 + +#### Scenario: Infrastructure is a thin adapter +- **WHEN** 调用 infrastructure 函数 +- **THEN** 函数只做协议转换和错误映射,不包含业务逻辑 + +### Requirement: Dependency direction is top-down +各层 SHALL 遵循从上到下的依赖方向:Router → Service → Domain / Repository / Infrastructure。下层不得依赖上层,同层不得循环依赖。 + +#### Scenario: No upward dependency +- **WHEN** 检查各层的 import 语句 +- **THEN** repository 不导入 service,domain 不导入 service 或 router,infrastructure 不导入 service + +#### Scenario: No circular dependency within same layer +- **WHEN** 检查同层模块的 import 语句 +- **THEN** 不存在 A 导入 B 同时 B 导入 A 的情况 + +### Requirement: Module declares layer and dependencies +新增或重构模块 SHALL 在模块顶部注释或 docstring 中说明所属层级和允许的依赖方向。 + +#### Scenario: New module has layer annotation +- **WHEN** 开发者新增一个后端模块 +- **THEN** 模块顶部包含 `# Layer: ` 注释或等效 docstring diff --git a/openspec/changes/backend-engineering-governance/specs/shared-constants/spec.md b/openspec/changes/backend-engineering-governance/specs/shared-constants/spec.md new file mode 100644 index 0000000..e098f60 --- /dev/null +++ b/openspec/changes/backend-engineering-governance/specs/shared-constants/spec.md @@ -0,0 +1,46 @@ +# Shared Constants + +## Purpose + +定义 `packages/shared/video_downloader_shared/` 作为跨模块常量、枚举和 DTO 的唯一真相源。 + +## ADDED Requirements + +### Requirement: Error codes defined in shared package +所有 `failure_code` 常量 SHALL 在 `packages/shared/video_downloader_shared/codes.py` 中统一定义,业务代码通过导入引用。 + +#### Scenario: New error code added to shared +- **WHEN** 开发者需要新增一个 `failure_code` +- **THEN** 先在 `codes.py` 中定义常量,再在业务代码中导入使用 + +#### Scenario: No hardcoded error code strings +- **WHEN** 检查 `apps/api/app/services/` 和 `apps/worker/worker/` 的代码 +- **THEN** 不存在硬编码的 `failure_code` 字符串字面量 + +### Requirement: Task state enum in shared package +任务状态枚举 SHALL 在 `packages/shared/video_downloader_shared/states.py` 中定义,业务代码通过导入引用。 + +#### Scenario: State transitions use shared enum +- **WHEN** 业务代码需要引用任务状态 +- **THEN** 导入 `TaskState` 枚举,不使用字符串字面量 + +### Requirement: Platform IDs defined in shared package +平台标识常量 SHALL 在 `packages/shared/video_downloader_shared/platforms.py` 中定义。 + +#### Scenario: Platform ID lookup uses shared constant +- **WHEN** 业务代码需要引用平台 ID +- **THEN** 导入 `platforms.py` 中的常量,不使用字符串字面量 + +### Requirement: Cross-module DTOs in shared package +跨 API 和 Worker 共享的数据传输对象 SHALL 在 `packages/shared/video_downloader_shared/dto.py` 中定义。 + +#### Scenario: DTO shared between API and Worker +- **WHEN** API 和 Worker 需要交换结构化数据 +- **THEN** 使用 `dto.py` 中定义的 DTO 类,不各自定义兼容结构 + +### Requirement: Shared package has no app dependencies +`packages/shared/` SHALL 不依赖 `apps/api/` 或 `apps/worker/` 中的任何模块。 + +#### Scenario: No upward import from shared +- **WHEN** 检查 `packages/shared/` 的 import 语句 +- **THEN** 不存在导入 `apps.api` 或 `apps.worker` 的语句 diff --git a/openspec/changes/backend-engineering-governance/tasks.md b/openspec/changes/backend-engineering-governance/tasks.md new file mode 100644 index 0000000..312516c --- /dev/null +++ b/openspec/changes/backend-engineering-governance/tasks.md @@ -0,0 +1,56 @@ +## 1. OpenSpec Change Artifacts + +- [x] 1.1 创建 `openspec/changes/backend-engineering-governance/proposal.md` +- [x] 1.2 创建 `openspec/changes/backend-engineering-governance/design.md` +- [x] 1.3 创建 `openspec/changes/backend-engineering-governance/specs/architecture-boundaries/spec.md` +- [x] 1.4 创建 `openspec/changes/backend-engineering-governance/specs/shared-constants/spec.md` +- [x] 1.5 创建 `openspec/changes/backend-engineering-governance/tasks.md` + +## 2. PRD 和 Plan 文档 + +- [x] 2.1 创建 `docs/prd/08-后端工程规范与架构治理.md` +- [x] 2.2 创建 `docs/plans/13-后端工程规范与架构治理计划.md` + +## 3. Shared 真相源 + +- [ ] 3.1 创建 `packages/shared/video_downloader_shared/codes.py`,定义所有 `failure_code` 常量 +- [ ] 3.2 创建 `packages/shared/video_downloader_shared/platforms.py`,定义平台 ID 常量 +- [ ] 3.3 创建 `packages/shared/video_downloader_shared/dto.py`,定义跨模块 DTO +- [ ] 3.4 更新 `packages/shared/video_downloader_shared/__init__.py` 导出新模块 + +## 4. 分层目录骨架 + +- [ ] 4.1 创建 `apps/api/app/domain/__init__.py`,写入层级职责说明 +- [ ] 4.2 创建 `apps/api/app/repositories/__init__.py`,写入层级职责说明 +- [ ] 4.3 创建 `apps/api/app/infrastructure/__init__.py`,写入层级职责说明 + +## 5. 架构边界测试 + +- [ ] 5.1 创建 `apps/api/tests/test_architecture_boundaries.py` + - 验证 router 不直接导入 ORM 模型 + - 验证 domain 不导入基础设施 + - 验证 repository 不含业务逻辑模式 +- [ ] 5.2 创建 `apps/api/tests/test_shared_constants.py` + - 验证 services 和 worker 中无硬编码错误码字符串 + +## 6. 复杂度控制 + +- [ ] 6.1 创建 `scripts/check_complexity.sh`,检查 Python 文件行数 +- [ ] 6.2 将复杂度检查集成到 `npm test` + +## 7. 文档与脚本更新 + +- [ ] 7.1 更新 `docs/prd/README.md` 添加 PRD08 条目 +- [ ] 7.2 更新 `docs/plans/README.md` 添加 PLAN13 条目 +- [ ] 7.3 更新 `scripts/validate-repository.sh` 添加新文件检查 + +## 8. 推广 Specs 到 Baseline + +- [ ] 8.1 推广 `architecture-boundaries` spec 到 `openspec/specs/architecture-boundaries/spec.md` +- [ ] 8.2 推广 `shared-constants` spec 到 `openspec/specs/shared-constants/spec.md` + +## 9. 验证 + +- [ ] 9.1 运行 `npm test` 确认所有测试通过 +- [ ] 9.2 运行 `bash scripts/validate-repository.sh` 确认仓库结构检查通过 +- [ ] 9.3 运行 `bash scripts/check_complexity.sh` 确认复杂度检查通过 diff --git a/scripts/validate-repository.sh b/scripts/validate-repository.sh index 9ccfadc..133e139 100755 --- a/scripts/validate-repository.sh +++ b/scripts/validate-repository.sh @@ -54,6 +54,7 @@ required_files=( "docs/prd/03-异步下载任务主链路.md" "docs/prd/04-MinIO产物归档与下载交付.md" "docs/prd/05-自部署运行与环境复用.md" + "docs/prd/08-后端工程规范与架构治理.md" "docs/design/01-个人自部署万能视频下载器技术设计.md" "docs/plans/01-URL协议与地址安全计划.md" "docs/plans/02-错误语义与日志脱敏计划.md" @@ -65,6 +66,7 @@ required_files=( "docs/plans/08-预签名下载与过期清理计划.md" "docs/plans/09-本机开发与依赖复用计划.md" "docs/plans/10-DockerCompose部署与健康检查计划.md" + "docs/plans/13-后端工程规范与架构治理计划.md" "docs/acceptance/01-个人自部署万能视频下载器验收方案.md" "docs/operations/01-个人自部署万能视频下载器运行与部署.md" "docs/operations/02-OpenAPI契约与前端生成协作.md"