|
|
# Grill: iTi-Flask 框架重构
|
|
|
Date: 2026-05-08
|
|
|
|
|
|
## Intent
|
|
|
iTi-Flask 要成为多个长期业务项目复用的后端基座。它需要提供稳定的系统域、模块边界、服务调用实践和项目模板,同时不承载具体业务逻辑。
|
|
|
|
|
|
## Constraints
|
|
|
- 业务项目必须依赖版本化 Git tag,不复制框架源码。
|
|
|
- 业务项目可以扩展框架,但不能修改或覆盖框架实现。
|
|
|
- 框架保留系统域 API,并作为核心默认能力。
|
|
|
- 框架必须保持轻量。它不是微服务平台。
|
|
|
- 生产只从 `main` 发布。
|
|
|
- Python 基线是 `>=3.11`。
|
|
|
- 第一版服务调用只支持 HTTP JSON。
|
|
|
- 第一版任务运行器是单进程能力。
|
|
|
- 前端构建产物不作为框架内容发布。
|
|
|
- 运行时数据库和生成物不是源码资产。
|
|
|
|
|
|
## Key decisions
|
|
|
- Decision: 使用 Python 包 + Copier 模板。Reason: 多个业务项目需要共享运行时代码和一致项目骨架。Alternative considered: 只用 Copier 复制代码。
|
|
|
- Decision: 先用 Git tag 发布。Reason: 在私有 PyPI 建立前,私有 Git tag 已够用。Alternative considered: 本地路径依赖或一开始就上私有 PyPI。
|
|
|
- Decision: 系统域留在 core,并默认启用。Reason: 用户、角色、菜单、部门、字典、配置、文件、认证、审计日志和用户扩展属性是后台基座共同能力。Alternative considered: 把系统域拆成独立包。
|
|
|
- Decision: 业务项目只能扩展框架行为。Reason: 直接覆盖会破坏后续升级。Alternative considered: 本地覆盖和 monkey patch。
|
|
|
- Decision: ERP 离开 core,成为独立 Gateway 服务。Reason: ERP 是多个业务系统共享的 Monitor API 和 ODBC 解释层,应只有一个口径。Alternative considered: ERP 作为 core module 或每个项目各接一次。
|
|
|
- Decision: 框架同时支持进程内 module 和 service client。Reason: module 是代码边界,service 是部署边界。Alternative considered: 全部微服务化。
|
|
|
- Decision: module 跨调用走 facade。Reason: 保留 Python 调用的简单性,同时避免穿透内部实现。Alternative considered: module 之间全走 HTTP 或随意 import。
|
|
|
- Decision: 业务项目模型集中在结构化 `models/` 树中,并保留一条 migration 流。Reason: 降低重复 migrations,同时支持多人按 module 协作。Alternative considered: module 自有 migrations。
|
|
|
- Decision: migrations 版本化并提交。Reason: 多人开发和生产升级需要可复现数据库历史。Alternative considered: 忽略 migrations 和手工改库。
|
|
|
- Decision: 业务项目使用 `main` 做生产、`dev` 做集成、个人分支做开发。Reason: migration 顺序必须在生产前收敛。Alternative considered: 生产从个人分支发布。
|
|
|
- Decision: migration 文件名使用日期时间、revision 和 message slug。Reason: 作者名放在自由 message 中,不强制业务域格式。Alternative considered: 固定 domain/action message 格式。
|
|
|
- Decision: 框架系统迁移复制或同步到业务项目 migrations。Reason: 生产只保留一个 `flask db upgrade` 路径。Alternative considered: 框架和业务两套迁移命令。
|
|
|
- Decision: seed 使用 Python,幂等,只写系统初始数据。Reason: 运行时 SQLite 文件不是源码资产,MySQL 等数据库应由代码初始化。Alternative considered: 保留 dev 数据库或使用 JSON/SQL seed。
|
|
|
- Decision: 后台 API 可以保留 envelope 和业务失败 HTTP 200。服务 API 使用真实 HTTP 状态码。Reason: 重试、熔断和监控需要真实状态码语义。Alternative considered: 所有 API 都继续 HTTP 200。
|
|
|
- Decision: 同时保留显式 module protocol 和现有 runtime plugin 思路。Reason: 业务模块是稳定应用代码,插件是部署时可选扩展。Alternative considered: 只保留插件扫描或不保留插件能力。
|
|
|
- Decision: 第一阶段必须让 service client 和 task runner 可用。Reason: 架构要可验证,不能只停在契约。Alternative considered: 第一阶段只写契约。
|
|
|
|
|
|
## Surfaced assumptions
|
|
|
- ERP 不是普通业务逻辑,而是面向既有外部系统的公共 Gateway。
|
|
|
- 团队更常用个人 Git 分支,而不是严格功能分支。
|
|
|
- 历史上可能存在手工生产库变更,但目标纪律是 migration-based。
|
|
|
- 开发和生产后续可能使用 MySQL 或其它数据库,所以 SQLite 快照不是可靠 seed 策略。
|
|
|
- 轻量微服务只服务于职责边界、协作和复用,不追求 service mesh、服务发现、分布式事务或平台复杂度。
|
|
|
|
|
|
## Out of scope
|
|
|
- 服务发现。
|
|
|
- Kubernetes service mesh。
|
|
|
- 分布式事务。
|
|
|
- Saga 编排。
|
|
|
- gRPC。
|
|
|
- 消息总线平台。
|
|
|
- 自动弹性伸缩平台。
|
|
|
- 多租户网关。
|
|
|
- 完整 async service client。
|
|
|
- 分布式任务锁或 exactly-once 保证。
|
|
|
- 内置前端后台应用。
|