Files
ephron-ren-prd/prd-app-factory-config-unification-complete-checklist.md

16 KiB
Raw Permalink Blame History

PRD — App Factory / Config Unification 完整优化清单

背景

ephron.ren 当前已经完成了一轮 app factory / config shared 化重构,远端相关主线提交为:

  • 14484ca — Add shared modules: app factory, config base, error handlers with tests
  • 6d94b08 — Refactor all apps to use shared config and app factory
  • 378a4e5 — Update home pages route and add test templates

现状不是“完全没做”,而是:

  • shared 基础设施已经建立
  • 五个服务已经基本接入 shared app/config 骨架
  • 但整体状态仍停留在“完成了第一轮统一”,还没达到“结构稳定、依赖完整、验证闭环、后续不易漂移”的完成态

因此这份 PRD 不再按 Batch 1 / Batch 2 / follow-up 拆分,而是直接给出 从当前状态到完成态的完整优化清单

这份文档的目标是:开发只看这一份,就知道哪些必须改、哪些建议改、哪些不要顺手乱改,以及最后怎么验收。


总体结论

当前重构方向是对的,但还差四类关键补完:

  1. 依赖契约没补齐

    • shared.app_factory 已把 slowapi 变成公共硬依赖
    • 但测试/安装链路没有完全同步,导致服务入口级测试会直接 import 失败
  2. 导入边界不稳定

    • main.py / config.py 乃至 route/service/test 里仍保留大量 sys.path.insert(...)
    • 说明 shared 化了逻辑,但没收住模块边界
  3. admin 路由守卫仍是服务内私有实现

    • home 已有实际 guard
    • 但 guard 骨架没 shared 化,未来其他服务继续扩展时很容易复制漂移
  4. 验证层只证明了 shared 能跑,没充分证明服务入口能跑

    • 当前 shared tests 通过,不代表五个服务入口都可稳定导入/装配

优化目标

完成本清单后,应该达到以下状态:

  1. 五个服务的 app 创建与 config 读取,已经统一且可维护
  2. shared app factory 的依赖在开发、测试、部署三条链路中一致成立
  3. Batch 2 涉及的 10 个 main.py/config.py 文件不再各自复制路径注入模板
  4. home 的 admin 守卫公共骨架进入 shared 层,后续可供其他服务复用
  5. /health 只由 shared app factory 统一归口
  6. 测试不仅覆盖 shared 合同,还覆盖服务入口装配合同
  7. 改完后可以明确说:这轮统一已经从“代码抽取”升级为“工程约束已建立”

非目标

本轮不要做以下事情:

  • 不重写整站认证体系
  • 不全量重构所有 route/service/test 的导入结构
  • 不一次性把所有 auth/token 逻辑全部抽进 shared
  • 不改业务权限模型
  • 不改 service API 设计
  • 不迁移仓库目录结构
  • 不强制一步到位改成完整 package 化(如全面 pyproject/installable layout
  • 不顺手做与本次重构无关的业务修复

原则很简单:只修 app/config 统一后暴露出来的结构性缺口,不把这轮工作膨胀成全仓重构。


一、必须修改P0 / P1

P0-1. 补齐 slowapi 依赖契约

问题

shared.app_factory.py 已直接依赖:

  • slowapi._rate_limit_exceeded_handler
  • slowapi.errors.RateLimitExceeded

这意味着:

  • 只要服务入口用 shared factory
  • slowapi 就不再是“某服务可选依赖”
  • 而是整个服务启动路径的公共硬依赖

复审中的实际验证结果也说明了这个问题:

python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q

通过;但执行:

python -m pytest auth/tests/test_security_hardening.py auth/tests/test_login_redirect_flow.py -q

在 collection 阶段报:

ModuleNotFoundError: No module named 'slowapi'

必须改什么

  1. 检查项目实际依赖入口
    • 根依赖文件
    • 测试依赖入口
    • 部署安装入口
  2. 保证 slowapi 在这三条链路里都被安装
  3. 如果存在多个 requirements 文件,必须统一,不允许某条链路漏掉
  4. 若 CI 有单独安装脚本,也必须同步更新

验收标准

以下命令在标准开发环境中必须通过:

python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q
python -m pytest auth/tests/test_security_hardening.py auth/tests/test_login_redirect_flow.py -q

且不允许再出现:

ModuleNotFoundError: slowapi

P0-2. /health 必须只保留 shared app factory 单一归口

问题

app factory 统一后的设计目标之一,就是 /health 由 shared factory 统一注册。

这件事必须做到“单一所有权”,否则后续一旦 health 响应结构升级,很容易出现某个服务还是旧实现。

当前重点确认点:

  • home/src/routes/pages.py 不应再重复定义 /health
  • 其他服务也不应在页面路由层残留重复 health endpoint

必须改什么

  1. 全仓检查 /health 定义位置
  2. 保留 shared app factory 中的统一实现
  3. 删除各服务 pages/routes 中的重复实现
  4. 保证最终每个服务的 /health 都来自同一装配路径

验收标准

  • 服务层页面路由文件中不再重复定义 /health
  • GET /health 行为仍与现有外部契约兼容
  • 相关 shared / entrypoint tests 通过

P1-1. 收敛 Batch 2 目标文件中的 sys.path.insert(...) 残留

问题

本轮 shared 化后,以下 10 个核心文件本身仍保留路径注入模板:

  • auth/src/config.py
  • blog/src/config.py
  • canvas/src/config.py
  • prompt/src/config.py
  • home/src/config.py
  • auth/src/main.py
  • blog/src/main.py
  • canvas/src/main.py
  • prompt/src/main.py
  • home/src/main.py

这说明当前统一只是“逻辑 shared 化”,不是“导入边界稳定化”。

必须改什么

本轮至少把这 10 个文件收住,不要求一次性清理全仓。

推荐两种方案里选一种:

方案 A抽统一 import bootstrap helper推荐

例如新增:

  • shared/imports.py

提供统一函数,例如:

def ensure_project_root(file_path: str, levels: int = 3) -> None: ...

让入口/配置文件统一调用 helper不再各自复制

project_root = Path(...)
if str(project_root) not in sys.path:
    sys.path.insert(0, str(project_root))

方案 B修正运行/测试约定,让入口文件不再自行改 path

如果当前项目运行方式允许,也可以通过统一 cwd / pytest path / uvicorn 启动约定来消除核心入口内的 path hack。

但从风险和改造成本看,本轮更推荐方案 A。

最低要求

至少保证上面列出的 10 个文件不再各自手写重复模板,哪怕先统一到一个 helper。

验收标准

  • 10 个核心文件中不再散落重复的 path 注入模板
  • 改完后这 10 个文件仍然可导入
  • 不引入新的循环依赖

P1-2. 将 home/admin 路由守卫的公共骨架提取到 shared

问题

当前 home/src/routes/admin.py 实际上已经有明确的服务端路由守卫:

  • 未登录跳转 auth 登录页
  • 带 redirect 返回
  • 已登录但无权限则拒绝/跳转
  • service token 禁止访问 home admin

这说明现在 有 guard,但它仍然是 home 服务内的私有实现。问题不在“有没有守卫”,而在“守卫骨架没有统一”。

如果未来 blog/canvas/prompt 继续加强 admin 规则,极容易再次复制 _require_auth(...) 模式,造成:

  • 登录跳转策略漂移
  • redirect 处理方式漂移
  • service token admin 访问策略漂移

必须改什么

  1. shared/ 新增 admin guard helper命名可调整
  2. helper 至少负责这三件事:
    • 未登录时的登录跳转 URL 拼装
    • service token 是否允许访问 admin 的统一策略
    • 权限失败时的统一返回约定
  3. home/src/routes/admin.py 改为消费 shared helper
  4. 先落地 home不要求本轮同步改完所有服务 admin

不需要在本轮做的事

  • 不要求把所有业务权限判断都抽进去
  • 不要求统一所有服务特有的资源级权限逻辑
  • 不要求一次性重写 blog/canvas/prompt 的 admin 认证结构

验收标准

  • home/src/routes/admin.py 不再保留大段内联 guard 骨架模板
  • shared helper 能表达登录跳转 / redirect / service token admin 禁入策略
  • home 现有 admin 行为无回归

P1-3. 补服务入口装配级合同测试

问题

当前 shared 测试通过,能证明:

  • shared config base 基本可用
  • shared error handlers 基本可用
  • shared app factory 基本可用

但这不等于五个真实服务入口都还能导入、装配、挂 router、提供 /health

现在缺的是一类非常轻量但非常关键的测试:服务入口装配合同测试

必须改什么

新增一组轻量测试,重点不是测业务,而是测“服务入口仍站得住”。

建议覆盖:

  1. 五个服务入口都可导入
    • auth/src/main.py
    • blog/src/main.py
    • canvas/src/main.py
    • prompt/src/main.py
    • home/src/main.py
  2. 导入后存在 app
  3. app.routes 中包含 /health
  4. docs 开关仍符合 dev/prod 预期
  5. static mount / router 注册没有在统一重构中丢失

验收标准

新增测试能在这些情况给出明确失败信号:

  • shared factory 依赖缺失
  • 入口导入断裂
  • /health 丢失
  • app factory 装配未生效

二、建议修改P2

这些不是“本轮不改就一定阻断”的问题,但建议一起纳入开发检查表。

P2-1. 统一依赖声明与文档说明

建议改什么

  1. 在依赖文件中明确标注 shared app factory 的新增公共依赖
  2. 若项目有 README / 开发说明,补一句:
    • 使用 shared app factory 的服务,需要安装公共依赖集
  3. 若有部署脚本或 bootstrap 脚本,检查是否与 requirements 同步

目的

避免开发者只看代码不看变更背景,导致本地能跑 shared tests、却跑不动服务 tests。


P2-2. 为 ENV / ENVIRONMENT 兼容关系补显式测试

背景

当前 home 仍兼容历史 ENVIRONMENT,这是对的,但兼容行为需要被明确测试下来,否则后面很容易被“顺手清理”掉。

建议增加的断言

至少覆盖:

  1. 只设置 ENVIRONMENT=development 时,home 仍进入开发模式
  2. 同时设置 ENV=productionENVIRONMENT=development 时,以 ENV 为准
  3. 未设置时默认回落到 production

目的

把“历史兼容”从口头约定变成测试契约。


P2-3. 统一 config summary 输出风格,但不要统一业务字段含义

建议改什么

  1. 保持 summary 打印格式统一
  2. 但不要为了“字段看起来一样”而强行让各服务展示完全同一组字段

原则

  • 统一的是打印骨架
  • 不是每个服务必须展示完全一样的业务字段

例如:

  • blog 仍应展示 CONTENT_DIR / CACHE_DIR
  • home 仍应展示 AUTH_BASE_URL / SERVICE_PORT

目的

避免“形式统一”侵入业务语义。


P2-4. 为后续全仓导入治理保留清单,但不要本轮扩大范围

已确认存在的后续技术债

除了 10 个核心文件外route/service/test 层仍有较多 sys.path.insert(...)

建议处理方式

  • 本轮只在 PRD 中登记为后续项
  • 不要在这轮顺手扩展到全仓清理

原因

这类改动跨面太大,容易把“当前可收敛的重构收尾”演变成“无法控风险的大扫除”。


三、明确不要修改的内容

以下内容在本轮必须明确禁止“顺手改”:

  1. 不要重写业务权限模型

    • 例如 owner/admin/user 的权限体系
    • 不属于 app/config 统一收尾范围
  2. 不要把所有 auth/token 校验逻辑一次性抽到 shared

    • 当前只要求抽 admin guard 公共骨架
    • 不是全站认证大重构
  3. 不要一口气清理所有 sys.path.insert(...)

    • 只收缩 Batch 2 涉及的核心入口/配置文件
    • 其余留待后续技术债专项
  4. 不要改 service API / 路由外部契约

    • URL 不变
    • 响应形态不因为这轮统一而改变
  5. 不要顺手改目录结构

    • 不迁移为全量 package layout
    • 不移动服务目录
  6. 不要把 config 层业务逻辑抽进 shared

    • 例如 blog 的缓存目录创建
    • canvas 的 content 目录自动创建
    • 这些仍应由各服务自己负责

四、建议实施顺序

虽然这份文档不再按阶段写,但实际落地仍建议按风险从低到高推进:

1. 先补依赖契约

  • 先解决 slowapi 安装链路
  • 否则后续很多入口测试都没法真实验证

2. 再补入口合同测试

  • 先建立“导入/装配级失败能被看见”的机制
  • 避免边改边盲飞

3. 再收敛 10 个核心文件的 path hack

  • 控制范围,不碰全仓

4. 再抽 home/admin guard 骨架

  • 这是结构优化,不是依赖修复,放在后面更稳

5. 最后全量回归

  • shared tests
  • auth smoke
  • home tests
  • 新增 entrypoint tests

五、建议涉及文件

必改文件

  • shared/app_factory.py
  • shared/config_base.py(如需补 helper / 兼容逻辑 / 测试契约)
  • auth/src/main.py
  • blog/src/main.py
  • canvas/src/main.py
  • prompt/src/main.py
  • home/src/main.py
  • auth/src/config.py
  • blog/src/config.py
  • canvas/src/config.py
  • prompt/src/config.py
  • home/src/config.py
  • 依赖声明文件(按项目实际结构)

建议新增文件

  • shared/imports.py 或等价 helper
  • shared/admin_guard.py 或等价 helper
  • tests/test_service_entrypoints.py 或等价入口合同测试文件

只观察,不要求本轮修改

  • home/src/services/auth.py
  • 其他 route/service/test 中的历史 sys.path.insert(...)
  • 任何 service API / 业务 handler 的功能逻辑

六、完整验收命令

至少执行并记录以下验证:

cd /home/ubuntu/ephron.ren
git fetch origin
python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q
python -m pytest auth/tests/test_security_hardening.py auth/tests/test_login_redirect_flow.py -q

如果新增了入口合同测试,再执行:

python -m pytest tests/test_service_entrypoints.py -q

如果抽了 home admin guard helper再补

python -m pytest home/tests -q

如果有 shared 相关新增单测,也一并跑:

python -m pytest tests/test_shared_config_base.py tests/test_error_handlers.py tests/test_app_factory.py -q

七、完成定义

满足以下全部条件,才算这轮优化真正完成:

  1. slowapi 在开发、测试、部署链路中都已被视为公共硬依赖
  2. 服务入口级测试不再因 slowapi 缺失而 import 失败
  3. /health 已统一归口到 shared app factory
  4. Batch 2 涉及的 10 个核心 main.py/config.py 文件不再各自复制 path 注入模板,或至少统一到同一 helper
  5. home 的 admin 路由守卫公共骨架已进入 shared 层
  6. 新增服务入口装配合同测试,并实际通过
  7. shared 层测试 + auth smoke + home 相关测试均通过
  8. 外部行为未发生非预期变化:
    • URL 不变
    • docs_url 行为兼容
    • static mount 行为兼容
    • 404/500 页面行为兼容

八、一句话给开发的执行原则

这轮工作的本质不是“继续抽公共代码”,而是:

把已经做完的 shared 化,补成一套真正可依赖、可验证、可持续的工程约束。

判断标准也很明确:

  • 不是“代码看起来更整洁了”
  • 而是“入口真的能导入、依赖真的齐、guard 不再乱飘、测试真的能兜住回归”

做到这一点,这轮 app factory / config unification 才算真正收口。