Files
geMoldInsight/docs/BACKEND_MODULARIZATION_BLUEPRINT.md
T
2026-08-27 14:53:22 +08:00

908 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# geMoldInsight 后端模块化重构蓝图
> 目标:在**同一 Git 仓库**、**同一数据库**前提下,将当前代码结构整理为**可独立部署的多模块架构**,明确 gemold、inventory、frontend 的边界与演进路径。本文档是后续实施的权威蓝图,不是历史记录。
---
## 1. 背景与目标
当前项目已经从早期单体演进为“双应用 + 共享层”的形态:
- 业务模块:`src/moldinsight/`、`src/inventory/`
- 共享层:`src/shared/`
- 部署入口:`src/entrypoints/moldinsight.py`、`src/entrypoints/inventory.py`
- 前端工程:`frontend/`
从代码现状看,`moldinsight` 与 `inventory` 已基本无直接互引,说明模块边界已经初步形成;但同时,数据库模型、认证、配置、应用工厂等仍集中在 `shared/`,导致当前更接近“**模块化中的单体**”,而不是“**可独立部署的模块化单体**”。
本次蓝图的目标不是拆库、拆仓、微服务化,而是:
1. 保持**单仓库(monorepo)**
2. 保持**单数据库**
3. 将后端正式整理为多个模块:
- **gemold**(模具分析 / moldinsight)
- **inventory**(进销存)
- **identity / platform**(共享基础设施与认证)
4. 保持前端为**独立模块工程**,并增强其对多部署模式的兼容
5. 支持三种部署模式:
- **unified**:gemold + inventory 同时部署
- **gemold-only**:仅部署 gemold
- **inventory-only**:仅部署 inventory
---
## 2. 范围与非目标
### 2.1 本次蓝图覆盖范围
- 后端模块边界
- 共享层职责重新定义
- 单数据库下的模型归属与表所有权
- 独立部署模式设计
- 前端 API 边界强化策略
- 分阶段迁移路径
- 风险与验证方法
### 2.2 非目标
本蓝图**不包含**以下方向:
- 多 Git 仓库拆分
- 多数据库拆分
- 分布式微服务重构
- 全量 DDD 重写
- 全量前端重构
- 事件驱动或消息总线主导的跨服务通信改造
换句话说,目标架构是:
> **单仓库 + 单数据库 + 多模块 + 可独立部署的 modular monolith**
而不是微服务系统。
---
## 3. 当前架构快照
### 3.1 后端现状
当前核心目录结构如下:
```text
src/
entrypoints/
moldinsight.py
inventory.py
shared/
app_factory.py
config/
database/
models/
services/
utils/
moldinsight/
api/
core/
services/
storage/
inventory/
api/
schemas/
services/
```
其中:
- [app_factory.py](../src/shared/app_factory.py) 已统一 CORS、日志中间件、startup/shutdown、认证路由、健康检查与 SPA fallback。
- [moldinsight.py](../src/entrypoints/moldinsight.py) 与 [inventory.py](../src/entrypoints/inventory.py) 已提供独立入口。
- [database.py](../src/shared/models/database.py) 同时包含 identity、moldinsight、inventory 三类模型,是当前最强耦合点。
### 3.2 前端现状
前端已是独立 Vite/Vue 工程:
```text
frontend/
src/
modules/
moldinsight/
inventory/
login/
users/
home/
router/
shared/
stores/
types/
```
特点:
- 已按页面/业务模块组织
- 已有共享 API 层雏形:[api-client.ts](../frontend/src/shared/api-client.ts)
- 已接入 OpenAPI 生成类型:[api.ts](../frontend/src/types/api.ts)
- 当前仍有大量相对路径 `/api/...` 与 raw `fetch()` 的同源假设
### 3.3 现状判断
当前架构可概括为:
> **HTTP 入口已分离,业务代码已分组,但平台层与数据层仍共享。**
因此,它适合继续朝“可独立部署的模块化单体”演进,而不适合直接跳到微服务。
---
## 4. 目标架构:可独立部署的模块化单体
### 4.1 架构原则
目标态采用以下原则:
1. **组合优先于复制**:不同部署模式通过组合不同模块得到,而不是复制多套代码。
2. **平台与业务分离**:`platform` 只承载技术性共享能力,不继续承载业务逻辑。
3. **模块所有权清晰**:每张表、每条 API、每个服务类都应有明确归属。
4. **单数据库但显式边界**:允许共享 DB,但不允许“共享数据库 = 没有边界”。
5. **统一前端,兼容多后端部署形态**:前端继续是一套工程,通过 API client 适配 unified / split deployment。
### 4.2 架构定位
目标架构不是“多个服务各自拥有数据库”的微服务,而是:
- 同一个仓库
- 同一个数据库
- 多个模块
- 多个部署入口
- 同一套迁移历史
- 同一套共享基础设施能力
这是一种适合当前项目阶段的**治理性重构**,而不是组织级拆分。
---
## 5. 目标目录结构
以下结构是**目标态**,不要求一次性到位:
```text
src/
platform/
app/
app_factory.py
lifecycle.py
middleware/
config/
auth/
database/
engine.py
session.py
migrations/
observability/
contracts/
api/
events/
shared_kernel/
types/
exceptions/
utils/
modules/
moldinsight/
application/
services/
use_cases/
domain/
models/
policies/
repositories/
infrastructure/
persistence/
storage/
adapters/
interfaces/
api/
schemas/
module.py
inventory/
application/
services/
use_cases/
domain/
models/
policies/
repositories/
infrastructure/
persistence/
adapters/
interfaces/
api/
schemas/
module.py
identity/
application/
domain/
infrastructure/
interfaces/
module.py
compositions/
unified_app.py
gemold_app.py
inventory_app.py
legacy/
main.py
```
### 5.1 当前目录到目标目录的映射
| 当前路径 | 目标路径 | 说明 |
|---|---|---|
| `src/shared/*` | `src/platform/*` | 共享基础设施重新命名与归位 |
| `src/moldinsight/*` | `src/modules/moldinsight/*` | gemold 业务模块 |
| `src/inventory/*` | `src/modules/inventory/*` | inventory 业务模块 |
| `src/entrypoints/*` | `src/compositions/*` | 部署组合层 |
| `src/main.py` | `src/legacy/main.py` | 过渡期兼容入口 |
### 5.2 为什么要引入 `compositions/`
当前 [entrypoints/](../src/entrypoints/) 已经承担“部署入口”角色,但语义更偏“启动文件”。
引入 `compositions/` 的意义是明确:
- 它不是业务模块
- 它不是平台能力
- 它是“**按部署模式装配模块**”的组合层
例如:
- `gemold_app.py`:平台 + identity + moldinsight
- `inventory_app.py`:平台 + identity + inventory
- `unified_app.py`:平台 + identity + moldinsight + inventory
---
## 6. 模块职责边界
### 6.1 platform
`platform` 是技术共享层,只承载**跨模块公共基础设施能力**。
#### 归属范围
- app factory / middleware / 生命周期
- config
- 数据库 engine / session / Alembic 接线
- auth / JWT / 权限
- 日志 / request_id / observability
- 通用 contracts / utilities
#### 可复用现有实现
- [app_factory.py](../src/shared/app_factory.py)
- [settings.py](../src/shared/config/settings.py)
- [database.py](../src/shared/database/database.py)
- [auth_service.py](../src/shared/services/auth_service.py)
- [auth_routes.py](../src/shared/services/auth_routes.py)
#### 规则
`platform` 不得继续吸收业务规则代码,否则新的 `platform` 会变成旧的 `shared`。
---
### 6.2 moldinsight(gemold)
`moldinsight` 模块负责模具分析、几何处理和分析结果生命周期。
#### 归属范围
- 上传、任务、历史、批量分析、成本估算、CAM、分析结果
- 几何分析、特征检测、模具方案、报告
- RustFS / 对象存储接入
- Celery 异步处理链路
#### 可复用现有实现
- [moldinsight/api/__init__.py](../src/moldinsight/api/__init__.py)
- [processing_service.py](../src/moldinsight/services/processing_service.py)
- [task_query_service.py](../src/moldinsight/services/task_query_service.py)
- [moldinsight/core/](../src/moldinsight/core/)
- [celery_app.py](../src/celery_app.py)
- [celery_tasks.py](../src/celery_tasks.py)
---
### 6.3 inventory
`inventory` 模块负责 ERP / 进销存类业务流程。
#### 归属范围
- 产品、物料、供应商、客户、仓库
- 库存、库存流水
- 销售订单、采购订单、财务、采购建议
- BOM / 采购需求推导
#### 可复用现有实现
- [inventory/api/__init__.py](../src/inventory/api/__init__.py)
- [inventory_service.py](../src/inventory/services/inventory_service.py)
- [sales_order_service.py](../src/inventory/services/sales_order_service.py)
- [purchase_order_service.py](../src/inventory/services/purchase_order_service.py)
- [finance_service.py](../src/inventory/services/finance_service.py)
---
### 6.4 identity
`identity` 负责用户、角色、权限与认证授权。
#### 归属范围
- User / Role / Permission
- 登录、鉴权、管理员接口
- 统一授权能力
在物理目录上,identity 可以先靠近 platform;但逻辑上应从一开始就被视为独立边界,而不是“moldinsight 的一部分”或“inventory 的一部分”。
---
## 7. 允许的依赖方向
这是本蓝图最重要的治理规则之一。
### 7.1 依赖规则
```text
compositions -> platform
compositions -> modules/*
platform -> 不依赖业务模块
modules/*/interfaces -> modules/*/application
modules/*/application -> modules/*/domain
modules/*/infrastructure -> modules/*/domain
modules/*/interfaces -> platform
modules/*/infrastructure -> platform
```
### 7.2 跨模块约束
- `inventory` 不能直接 import `moldinsight.api` / `moldinsight.services`
- `moldinsight` 不能直接 import `inventory.api` / `inventory.services`
- `platform` 不得依赖任一业务模块
- 跨模块协作只能通过:
- 显式 contract / query service
- composition wiring
- 受控的共享数据库引用
### 7.3 当前代码与规则的差距
从代码检查看,当前 `moldinsight` 与 `inventory` 基本没有直接互引,这说明上述规则在业务代码层**已经接近成立**。
当前主要问题集中在:
- [database.py](../src/shared/models/database.py) 集中承载所有 ORM 模型
- [app_factory.py](../src/shared/app_factory.py) 同时知道通用平台逻辑与 moldinsight 专属 startup 行为
因此,后续实施的重点不是“切断业务模块互引”,而是“**把共享层与数据层的边界拉直**”。
---
## 8. 单数据库下的模型与表归属策略
### 8.1 核心原则
数据库继续保持为**一个 PostgreSQL 数据库**,但代码中的模型归属必须显式化。
也就是说:
> **数据库不拆,模型归属要拆。**
### 8.2 当前最强耦合点
当前 [database.py](../src/shared/models/database.py) 同时包含:
- identity / 平台相关模型
- moldinsight 业务模型
- inventory 业务模型
这会带来两个问题:
1. 代码层看不出模型所有权
2. 开发者更容易跨模块直接访问整套 ORM 图谱
因此,后续实施应优先将这个文件按归属拆分。
### 8.3 推荐表归属矩阵
#### platform / identity-owned
- users
- roles
- permissions
- user_roles
- role_permissions
- (可选)system_logs
#### moldinsight-owned
- stp_files
- geometry_data
- mesh_data
- html_files
- processing_tasks
- mold_cavity_data
- feature_detections
- design_recommendations
- analysis_metrics
#### inventory-owned
- products
- product_materials
- material_price_history
- material_suppliers
- suppliers
- customers
- warehouses
- inventory
- stock_movements
- purchase_orders
- purchase_order_items
- sales_orders
- sales_order_items
- finance_transactions
- finance_allocations
### 8.4 目标代码形态
推荐逐步演进到:
```text
platform/database/base.py
modules/identity/infrastructure/persistence/models.py
modules/moldinsight/infrastructure/persistence/models.py
modules/inventory/infrastructure/persistence/models.py
```
同时保持:
- 同一个 SQLAlchemy `Base`
- 同一个 metadata
- 同一条 Alembic 历史链
- 同一个数据库连接
### 8.5 跨模块 FK 的处理策略
当前存在真实跨模块业务桥:
- `STPFile.product_id -> Product.id`
这是合理的,因为它反映了真实业务关系:模具分析结果可以创建成品,并与 inventory 中的产品建立连接。
原则上允许保留这类稳定 FK,但应遵守:
1. 跨模块 FK 是**业务桥**,不是“任意跨模块查询”的许可
2. 模块之间应逐步通过 query service / repository contract 暴露需要的读取能力
3. 避免一个模块直接依赖另一个模块的整套 ORM 图谱
---
## 9. 部署模式设计
本项目必须支持三种部署模式。
### 9.1 unified
#### 组成
- platform + identity + moldinsight + inventory
#### 适用场景
- 本地开发
- 小团队部署
- 集成环境
- 一体化业务场景
#### 说明
新的 unified 组合层应接替旧 [main.py](../src/main.py) 的语义,但应通过 `compositions/unified_app.py` 的方式实现,而不是继续维持旧单体式入口。
---
### 9.2 gemold-only
#### 组成
- platform + identity + moldinsight
#### 适用场景
- 仅开放模具分析能力
- 分析服务独立扩容
- 单独部署算法/文件处理能力
#### 说明
- 只暴露 gemold 路由
- 可保留对共享数据库中 `products` 的受控引用
- RustFS / Celery 等专属基础设施只在该模式启用
当前入口基线:
- [moldinsight.py](../src/entrypoints/moldinsight.py)
---
### 9.3 inventory-only
#### 组成
- platform + identity + inventory
#### 适用场景
- 仅提供 ERP / 进销存能力
- 不需要模具分析链路的后台场景
#### 说明
- 只暴露 inventory 路由
- 不要求启动 RustFS 等 moldinsight 专属基础设施
当前入口基线:
- [inventory.py](../src/entrypoints/inventory.py)
---
### 9.4 当前共享工厂的边界问题
需要特别指出:当前 [app_factory.py](../src/shared/app_factory.py) 通过 `mount_html=True` 分支初始化 RustFS,这说明共享工厂仍然知道 moldinsight 模块专属基础设施。
这意味着:
- `shared` / `platform` 还不够纯
- 模块专属 startup hook 还没有完全从平台层剥离
后续实施时,这应是**优先解决的问题之一**:
> app factory 只负责通用平台装配;模块专属生命周期应由 module hook 或 composition layer 决定。
---
## 10. 前端 API 边界策略
### 10.1 设计原则
前端已经是独立工程,不需要再拆目录或拆仓。后续重点应放在:
- 强化域 API 边界
- 消除对单一同源 `/api/...` 的强依赖
- 兼容 unified / split deployment
### 10.2 当前可复用基础
当前前端已经具备良好基础:
- 域客户端入口:[api-client.ts](../frontend/src/shared/api-client.ts)
- 生成类型:[api.ts](../frontend/src/types/api.ts)
- 模块目录:[frontend/src/modules/moldinsight/](../frontend/src/modules/moldinsight/) 与 [frontend/src/modules/inventory/](../frontend/src/modules/inventory/)
因此,后续策略应是“**边界强化**”,不是“前端重写”。
### 10.3 建议的客户端边界
应继续收敛为:
- `authApi`
- `moldinsightApi`
- `inventoryApi`
并要求:
- 页面/组件尽量不直接写裸 `/api/...` 字符串
- multipart upload / download / export 也尽量通过域客户端封装
- 统一利用 OpenAPI 生成类型,而不是额外维护平行手写类型
### 10.4 支持两类部署配置
#### unified 模式
- `VITE_API_BASE_URL`
#### split 模式
- `VITE_AUTH_API_BASE_URL`
- `VITE_MOLDINSIGHT_API_BASE_URL`
- `VITE_INVENTORY_API_BASE_URL`
客户端实现上应支持:
- unified 下三个 client 指向同一个 base URL
- split 下各 client 指向不同服务
### 10.5 capability-aware UI
在 gemold-only 或 inventory-only 部署模式下,前端不应假设所有模块总是存在。
因此建议:
- 后端通过 `/health` 或单独 capability endpoint 暴露已启用模块信息
- 前端根据 capability 隐藏未部署模块入口或禁用对应页面
这样可避免“前端路由还在,但后端根本没部署”的硬失败场景。
---
## 11. 分阶段迁移计划
### Phase 0:蓝图冻结
目标:冻结目标结构与边界规则,防止迁移过程中继续长出新的耦合。
动作:
- 确认模块边界
- 确认依赖规则
- 确认三种部署模式
- 确认 shared/platform 的使用边界
---
### Phase 1:组合层标准化
目标:将部署模式从“若干入口脚本”升级为“正式的 composition layer”。
动作:
- 将 `entrypoints/*` 升级为 `compositions/*`
- 定义 `unified_app.py` / `gemold_app.py` / `inventory_app.py`
- 将 module-specific startup hooks 从通用 app factory 中迁出
重点问题:
- 当前 `mount_html -> RustFS connect` 属于模块专属逻辑,必须从 shared factory 中剥离
---
### Phase 2:platform 与业务代码分治
目标:把“共享基础设施”从“共享杂项”中真正分离出来。
动作:
- 从 `shared` 语义切换到更清晰的 `platform`
- 归位 config / auth / db / observability / utils
- 禁止新业务逻辑继续沉积到 `shared`
---
### Phase 3:ORM 按归属拆分
目标:解决当前最大的代码耦合源。
动作:
- 拆分 [database.py](../src/shared/models/database.py)
- 保持单 Base / 单 Alembic / 单 DB
- 先完成代码层归属拆分,再处理更深层的业务隔离
这是整个重构中最关键的一步。
---
### Phase 4:模块内部继续分层
目标:让每个模块内部的“路由 / 服务 / 领域 / 基础设施”边界更清晰。
动作:
- inventory:从现有 service 层继续向 `application / domain / infrastructure / interfaces` 拉开
- moldinsight:逐步理顺 `api / services / core / storage` 的边界
原则:
- 不要求一步完成 DDD 化
- 先把层次职责清楚,再考虑更纯粹的领域对象抽象
---
### Phase 5:建立跨模块集成规则
目标:把当前 ad hoc 跨表访问逐步收敛成显式桥接能力。
动作:
- 定义跨模块 query services / repository contracts
- 明确“从分析创建成品”等桥接能力的正式入口
- 收敛直接穿透跨模块 ORM 图谱的做法
重点桥接场景:
- `STPFile.product_id`
- [product_routes.py](../src/inventory/api/product_routes.py) 中的“按 task 创建产品”逻辑
---
### Phase 6:前端边界加固
目标:让前端真正适配 unified / split deployment。
动作:
- 强制域 API client 收口
- 支持 per-module base URL
- 支持 capability-aware UI
- 减少页面中裸 `fetch('/api/...')`
---
### Phase 7:部署与文档收尾
目标:使架构蓝图与部署事实一致。
动作:
- 固化 unified / gemold-only / inventory-only 部署说明
- 更新 README / deployment docs / frontend docs
- 明确旧入口、旧 Dockerfile、旧部署说明的地位(兼容 / 历史 / 废弃)
---
## 12. 风险与缓解措施
### 风险 1:共享模型文件继续隐藏领域归属
**现象**:所有模型集中在 [database.py](../src/shared/models/database.py)
**风险**:边界无法落实,任何模块都能自然跨领域访问
**缓解**:
- 先产出表归属矩阵
- 再拆 ORM 文件
- 拆归属优先于改业务逻辑
---
### 风险 2:`shared` 继续变成杂物间
**风险**:shared/platform 成为“不知道放哪就放这里”的位置
**缓解**:
- 以 `platform` 重新定义共享层
- 明确只有技术性共享能力可进入 platform
- 代码 review 中禁止新增业务逻辑进入 platform
---
### 风险 3:模块专属启动逻辑继续留在 app factory
**现象**:当前 `mount_html` 分支控制 RustFS 连接
**风险**:平台层继续了解业务模块内部基础设施
**缓解**:
- module lifecycle hook
- composition 层控制模块 startup/shutdown
- 通用 app factory 保持纯平台化
---
### 风险 4:独立部署后,前端仍假设所有模块存在
**风险**:前端路由可见,但后端未部署,造成运行时错误
**缓解**:
- capability metadata
- feature gating
- 前端按模块部署形态动态隐藏入口
---
### 风险 5:单数据库导致开发者随意跨模块查表
**风险**:虽然数据库未拆,但代码边界被绕开
**缓解**:
- 表归属规则文档化
- code review 要求说明跨模块访问理由
- 逐步通过 query services 收口
---
### 风险 6:大爆炸式目录迁移导致 import churn
**风险**:一次性移动所有目录,修改面过大,回归成本高
**缓解**:
- 分阶段迁移
- 保留兼容 import 过渡层
- 先稳定组合层与模型层,再做深层目录调整
---
## 13. 验证清单
### 13.1 结构验证
- `platform` 不 import 业务模块
- `moldinsight` / `inventory` 无直接 api/services 互引
- ORM 文件已按归属拆分,或至少表归属矩阵已文档化并被遵守
### 13.2 运行验证
#### unified
- 两类路由都能正常挂载
- 统一 health / auth / request-id 机制生效
#### gemold-only
- inventory 路由未挂载
- gemold 路由可用
- RustFS / Celery 等专属依赖正常初始化
#### inventory-only
- moldinsight 路由未挂载
- inventory 路由可用
- 不依赖 moldinsight 专属基础设施即可启动
### 13.3 数据验证
- 三种部署模式使用同一 migration head
- 单数据库中的跨模块 FK 保持有效
- Alembic 无分叉历史
### 13.4 前端验证
- unified 模式下,一个 base URL 即可跑通
- split 模式下,各模块 base URL 可独立配置
- 未部署模块能被隐藏或优雅失败
---
## 14. 文档联动更新计划
本蓝图批准后,以下文档应逐步对齐:
- [EVOLUTION_ROADMAP.md](./EVOLUTION_ROADMAP.md)
- [LINUX_SETUP.md](./deployment/LINUX_SETUP.md)
- [DEPLOY_PORT.md](./deployment/DEPLOY_PORT.md)
- [PORT_CONFIG.md](./deployment/PORT_CONFIG.md)
- [PORT_REFACTOR_SUMMARY.md](./deployment/PORT_REFACTOR_SUMMARY.md)
- `README.md`
- `frontend/README.md`
其中需要特别注意:
- `README.md` 当前仍主要描述旧单体结构,与当前实际代码已有明显漂移。
- 本文档应作为“当前目标架构”的权威蓝图,路线图与部署文档随后对齐。
---
## 15. 附录:实施热点文件
以下文件是后续实际重构时最关键的热点:
### 平台与组合层
- [app_factory.py](../src/shared/app_factory.py)
- [settings.py](../src/shared/config/settings.py)
- [database.py](../src/shared/database/database.py)
- [init_db.py](../src/shared/database/init_db.py)
- [moldinsight.py](../src/entrypoints/moldinsight.py)
- [inventory.py](../src/entrypoints/inventory.py)
- [main.py](../src/main.py)
### 模型与边界
- [database.py](../src/shared/models/database.py)
- [product_routes.py](../src/inventory/api/product_routes.py)
### gemold 模块
- [moldinsight/api/__init__.py](../src/moldinsight/api/__init__.py)
- [processing_service.py](../src/moldinsight/services/processing_service.py)
- [task_query_service.py](../src/moldinsight/services/task_query_service.py)
### inventory 模块
- [inventory/api/__init__.py](../src/inventory/api/__init__.py)
- [inventory_service.py](../src/inventory/services/inventory_service.py)
- [sales_order_service.py](../src/inventory/services/sales_order_service.py)
- [purchase_order_service.py](../src/inventory/services/purchase_order_service.py)
- [finance_service.py](../src/inventory/services/finance_service.py)
### 前端边界
- [api-client.ts](../frontend/src/shared/api-client.ts)
- [api.ts](../frontend/src/types/api.ts)
### 演进与部署参考
- [EVOLUTION_ROADMAP.md](./EVOLUTION_ROADMAP.md)
- [docker-compose.yml](../docker-compose.yml)
---
## 16. 最终结论
本项目当前最适合的演进路径不是微服务,而是:
> **单仓库 + 单数据库 + 多模块 + 可独立部署的 modular monolith**
它既能保留当前业务闭环(模具分析 → 成品 → BOM → 销售/采购/库存),又能逐步降低共享层和模型层的结构耦合。
后续实施时,应优先按以下顺序推进:
1. 固化组合层(deployment composition)
2. 纯化 platform/shared 边界
3. 拆分共享 ORM 文件
4. 再继续模块内部细分层次
5. 最后收口前端 API 与部署文档
这条路径风险最低、收益最大,也最符合当前代码现状与团队演进成本。