Files
dsh_ai1net_server/docs/集群与实例/项目代码_分层范式与迭代风险评估_20260916.md
T

206 lines
12 KiB
Markdown
Raw Normal View History

# 项目代码 · 分层范式与迭代风险评估(2026-09-16)
> **性质**:只读架构评估(实测探针,非印象判断)。⛔ 未改任何代码。
> **取证**:`D:\github\dsh_shenxian\src` 全量静态扫描(HEAD `4e3a1a4`)—— 58 个 `.ts`、14,053 行;统计目录规模、文件扇出、**跨目录依赖矩阵**(探针脚本内部聚合,只输出摘要)。
> **一句话判定**:**范式方向是对的(按域分目录 + 依赖稀疏 + 扇出低),但有两类会被大规模迭代放大的隐患** —— ① **4 组双向依赖**(含"基础层反向依赖业务层")② **缺领域层**,导致业务规则沉进 route 文件。
> **关于"改一处要不要读全仓"**:**现在还不用,但已经出现"改一处要连读 2–3 个大文件(约 2,500 行)"的模式**;覆盖网络是**最后一次能以低成本立规矩的机会**。
---
## 一、实测数据
### 1.1 目录规模
| 目录 | 文件 | 行数 | 占比 |
|---|---|---|---|
| `web`(routes + server + nginx) | 24 | 5,593 | **40%** |
| `supervisor` | 11 | 3,295 | 23% |
| `db` | 10 | 2,960 | 21% |
| `(root)`(config / isolation / crypto / cli / index) | 5 | 836 | 6% |
| `worker` | 2 | 708 | 5% |
| `fs` | 6 | 661 | 5% |
| **合计** | **58** | **14,053** | — |
### 1.2 最大的 12 个文件 —— **行数大 ≠ 耦合高**
| 行数 | 文件 | 内部依赖数(扇出) |
|---|---|---|
| 1,259 | `supervisor/orchestrator.ts` | **6** |
| 756 | `db/pg.ts` | **4** |
| 752 | `db/repo.ts` | **3** |
| 743 | `web/routes/business-plugins.ts` | **4** |
| 706 | `supervisor/proxy.ts` | 3 |
| 687 | `web/routes/skills.ts` | 3 |
| 514 | `worker/agent.ts` | 8 |
| 500 | `web/routes/whitelist.ts` | 4 |
**⇒ 关键读数:1,259 行的文件只依赖 6 个模块。** 说明它大是因为"**同领域逻辑都塞在一个文件里**",**不是**因为"牵扯面广"。对"改一处要不要读全仓"来说,这是**好消息**。
### 1.3 跨目录依赖矩阵(全部 23 条边)
| from → to | 次数 | | from → to | 次数 |
|---|---|---|---|---|
| web → supervisor | 11 | | supervisor → worker | 2 |
| web → fs | 9 | | supervisor → web | 2 ⚠️ |
| web → (root) | 4 | | worker → supervisor | 2 ⚠️ |
| web → db | 3 | | db → (root) | 2 |
| web → nginx | 1 | | (root) → web | 2 ⚠️ |
| fs → web | 3 ⚠️ | | (root) → fs | 2 |
| supervisor → db | 3 | | (root) → db | 1 ⚠️ |
| worker → fs | 3 | | fs → worker | 1 ⚠️ |
| worker → (root) | 2 | | fs → (root) | 1 |
| supervisor → (root) | 1 | | (root) → 其他 | — |
**⇒ 关键读数:整仓只有 23 条跨目录边,最粗的一条是 11 次。没有"上帝模块"。**
---
## 二、范式评估
### 2.1 合理之处(4 条)
1. **按域分目录**:`web`(入口/HTTP)· `supervisor`(进程生命周期)· `db`(存储)· `fs`(用户文件)· `worker`(远程节点)—— 边界与职责对得上。
2. **依赖稀疏**:23 条边、最大 11 —— 远低于同类单体项目。
3. **抽象缝存在**:`Spawner` 接口把"后端"抽出来了,route 层只依赖接口。
4. **文件头注释质量高**(这一点被低估):`agent.ts` 头部 8 行讲清"它是什么 / 四条纪律 / 引用设计章节",`spawner.ts` 同理。**这就是"不用读全仓"的现成机制** —— 它把"这个文件负责什么"变成可低成本获取的信息。
### 2.2 隐患(4 条,均有数据支撑)
| # | 隐患 | 数据 | 后果 |
|---|---|---|---|
| 1 | **双向依赖 4 组** | `web ↔ supervisor`(11/2)· `web ↔ fs`(9/3)· `supervisor ↔ worker`(2/2)· `fs ↔ worker`(1/3) | 改任一侧都要看另一侧 ⇒ **上下文成本翻倍**;且无法单独测试 |
| 2 | **基础层反向依赖业务层** | `(root) → web` 2 处 · `(root) → db` 1 处 · `db → (root)` 2 处 | 共享层(config 等)依赖上层 ⇒ **分层方向被破坏**,这是最该修的一条 |
| 3 | **缺领域层** | `web` 占 40% 行数;`business-plugins.ts` 743 行、`skills.ts` 687 行 | 业务规则沉在 route 文件里 ⇒ 改规则要读 route;同规则无法被 worker/CLI 复用;难以单测 |
| 4 | **`web` 层过重** | 24 文件 / 5,593 行 | 入口层变成事实上的"业务层",进一步加剧 #3 |
---
## 三、是否需要分层:**需要补两层,但不需要重构**
### 3.1 现状的实际分层(隐式)
```
web(routes) ──► supervisor / db / fs ──► (root: config / types)
▲ ▲ │
└────────────────────┴────────────────────────┘
(反向边,违规)
```
### 3.2 建议的目标分层(**方向规则要写下来**)
```
① 入口层 web/routes · cli (只做 HTTP/CLI 编解码)
② 领域层 domain/* ← 【新增】业务规则,纯函数优先
③ 能力层 supervisor · db · fs · worker · net/*
④ 基础层 config · types · crypto ← 【禁止反向依赖】
依赖方向:① → ② → ③ → ④,单向,⛔ 不得回指
```
- **补 ② 领域层**:把"业务规则"从 route 抽出。收益 = route 瘦身 + 可单测 + 可被 worker/CLI 复用。
- **补 `net/` 作为能力层的一员**(与 supervisor/db/fs 同级),**不是新的一层** —— 这样覆盖网络不会变成一个"横跨所有层的特权模块"。
- **把依赖方向写进架构文档/CODEBUDDY**:现在规则是隐式的,靠自觉。
---
## 四、「改一处要读全仓」的风险评估(**这是用户真正问的**)
### 4.1 分规模档看
| 规模 | 读全仓的代价 | 结论 |
|---|---|---|
| **现在**(58 文件 / 14k 行) | 约 15 万字符 ≈ 4–5 万 token | **可行但不必** —— 实际不需要读全仓 |
| **覆盖网络落地后**(+3~4 目录 / +5~8k 行) | 约 22k 行 ≈ 7–8 万 token | ⚠️ **开始变贵**,且依赖边会从 23 条涨到 40+ |
| **继续叠加**(游戏 / 房间层 / 发布层) | — | 🔴 **若不立规矩,会真变成"改一处读全仓"** |
### 4.2 真正的风险不在规模,在**边界模糊**
现在已经在发生:改 `business-plugins.ts`(743 行)时,**你无法只靠它自己判断"这条规则属于插件管理还是实例生命周期"** —— 因为没有领域层,规则一半在 route、一半在 `orchestrator.ts`(1,259 行)和 `repo.ts`(752 行)。
⇒ **一次改动实际要连读 3 个大文件 ≈ 2,500 行**,这才是成本所在,而**与仓库总规模无关**。
### 4.3 三条可落地的规矩(覆盖网络正好是载体)
1. **单向依赖**:`net/*` 不得依赖 `web/*`;`web` 可以依赖 `net`。**先定规则再写第一行代码** —— 现在新增目录的成本最低。
2. **契约前置**:先写接口与类型,再写实现。会合中继拆分方案的 **S0 已经是这个做法**(纯新增 `src/net/rendezvous.ts` / `reachability.ts`,零行为变化)—— 把它变成制度,而不是一次性动作。
3. **文件头注释制度化成"模块索引"**:现有头注释写得很好,只要**规定"每个模块目录入口必须写明:职责 / 依赖谁 / 被谁依赖"**,就能把"读全仓"降级为"读索引 + 读相关模块"。
4. **⛔ 禁止基础层反向依赖**:`config` / `types` 不得 import `web` / `db` —— 现在有 3 处,趁早清掉。
### 4.4 一句话回答
> **现在不会**"改一处读全仓";**但已经会**"改一处读 2–3 个大文件";**覆盖网络之后如果不定依赖方向,就会真的变成读全仓。**
> ⇒ **需要做的不是重构,而是"立规矩 + 补一层 domain"。**
---
## 五、本机纳入测试环境(你已给的许可)
**定位**:本机(Windows 开发机)= **客户端类型的第一个测试节点**,正好补上可行性评估的**缺口 1**("dsh 在 Windows 上能否起实例 + 实例内 bash 是否可用"**至今未实测**)。
**形态判定**:Windows 无 bwrap / systemd / uid 隔离 ⇒ **只能走 `soft` 模式**(实例 = 裸子进程)—— 而这**正好就是客户端形态的目标模式**,不是降级。
**可先验的三件事(都不动服务器)**:
1. 本机起一个实例(`soft` 模式)⇒ 实例页 200;
2. 实例内跑一次 bash 工具 ⇒ 验证 dsh 在 Windows 的**沙箱后端行为**(这是全案唯一"尚无证据"的技术点);
3. 本机与 47 / 106 之间的**真实网络画像**(NAT 类型 / 打洞可行性 / 与中继的 RTT 与 jitter)—— 直接填"最该先测的三项"里的两项。
⚠️ 两点注意:① 本机是**唯一的开发机**,起实例会占资源,建议用最小配额;② 本机作为"客户端节点"参与网络时,**不得**顺手把它接进现有生产链路(要独立形态、独立开关)。
---
## 六、我选了什么(可推翻)
1. **不重构,只做两件事**:补 `domain` 层 + **把"单向依赖"写成书面规则**(四层:entry → domain → capability → base)。
2. **`net/` 定位为能力层的一员**(与 supervisor/db/fs 同级),不是特权跨层模块。
3. **把 S0 的"契约前置"制度化** —— 每个新增模块先出接口与类型文件。
4. **清掉 3 处基础层反向依赖**(`(root) → web` 2 + `(root) → db` 1),列为独立小任务。
5. **本机作为第一个客户端测试节点**,先做 §五 的三件验证。
---
## 七、本次未做
- ⛔ 未改任何代码、未动服务器、未写文档库(全局执行锁被 `修复轮-决策方法-2b` 占用)。
- 探针脚本落 `_中间产物_待清理/_arch_probe.py`(可复跑;结论已固化到本文件)。
- 📌 未新增上抛项。
---
# 八、执行计划与实际落地(2026-09-16 13:2x 追加)
## 8.1 🔴 勘误:§2.2 隐患②「基础层反向依赖 3 处」**不成立**
**复核命令**(只读):
```bash
grep -n "from '\./\(web\|db\|fs\|supervisor\|worker\|net\)/" src/*.ts
```
**只命中 `src/cli.ts`**(5 条);`config.ts` / `crypto.ts` / `isolation.ts` / `index.ts` **零 import**。
⇒ **根因**:上一轮统计把 **`cli.ts` 误算进了"基础层"**。它是**入口层①** —— import `db`/`fs`/`web` 属 **① → ③ 的合法方向**。
⇒ **结论修正**:
① 真实违规 = **0 处** ⇒ §4.3 第 4 条「清掉 3 处基础层反向依赖」**撤销**(没有可清的);
② §2.2 隐患② 应改写为「**边界模糊**」——真实成本在 `web/routes` 里沉着的业务规则(§4.2 那条),**不在依赖方向**。
⇒ **教训与 S0 的两处勘误同类:接手前人结论,先做一次最小取证。**
## 8.2 ✅ P0 已落地(本机 · 2 个文件)
| 文件 | 改动 |
|---|---|
| `docs/architecture.md` | **新增**:四层定义 + 依赖方向 `①→②→③→④ 单向,⛔ 不得回指` + **R1–R4 判据**(怎么算违反)+ 三条工程纪律(契约前置 / 模块头注释 = 索引 / 动手前自查)+ §3 现状实测 + §4 优先级 |
| `README.md` | 文档表**新增一行**指向它("动手改 `src/` 前读一遍") |
- **验收**:规则可从 `README → docs/architecture.md` 两级直达;四层归属带**可判定的判据**,不是口号。
- **回滚**:删 `docs/architecture.md` + 删 `README.md` 那一行。
- **为什么先做它**:零代码风险,而"扩张期走丢方向"的回头成本高得多(§四 4.1 的风险分档)。
- ⏳ **未做**:git commit(未授权)。
## 8.3 剩下的两件(均需**先出清单**,命中 R7)
**P1 · 补 `src/domain/*`** —— 把 `web/routes` 里的业务规则抽出来。
⚠️ **行为敏感重构**(不是纯类型);影响面 **>10 文件** ⇒ 动工前先出受影响清单。
判据(做它的唯一理由):让"改插件管理规则"不再需要连读 `orchestrator.ts`(1,259) + `repo.ts`(752)。
**P2 · 模块头注释补全** —— 按 `docs/architecture.md §2.2` 给各模块入口补「职责 / 依赖谁 / 被谁依赖」。
批量改 >10 文件 ⇒ 同样先出清单。
> **顺序建议**:**P0 ✅ → 覆盖网络 S1–S4 → P1**。
> P1 **不阻塞**覆盖网络;反过来,覆盖网络的新模块(`net/*`)正好是「契约前置」的第一个样板。