Files
dsh_shenxian/dsh-server-docs/04-调整方案/121-项目代码-分层范式与迭代风险评估.md
T
admin 04776af4b1 docs: 工作区根项目文档批量入仓(04-调整方案 113–128 / 交接单 T09–T21 / ops / archive)
起因:用户 2026-09-17 明确「所有文档都要同步,都放在开发仓库 docs 对应文件夹下」。
判据:工作区根 *.md 中在仓库(git ls-files --quotepath=false)搜不到的那些。

入仓 33 份(一律复制,工作区根原件保留不动,避免引用断链):
- 04-调整方案/113–128(16 份 · 原子占号后落盘):覆盖网络 传输方案取舍 / 应用场景与待完善清单 /
  插件化vs改内核 / 问题逐条推演 / 参数表与观测口径;会合中继拆分取证与改造方案;
  集群化改造方案 Manager-Worker;跨节点迁移与节点自举;项目代码分层范式与迭代风险评估;
  搬运与共享重建方案 guest w47→w106;方案规划方法提炼;文档无效信息审计报告;
  会话接续机制复盘与修复;会话接续规范;dsh 客户端化部署方案;dsh 桌面客户端开发方案
- 交接单/archive/交接单-已完成/T09–T21(13 份 · 覆盖网络线已完成单归档)
- ops/(2 份运行态指针:接续入口 / 接续包 · 覆盖网络线)
- archive/(2 份临时与内部简报)

已排除(无需重复入仓):覆盖网络线 10 份方案正文已入档案 103–112(文件名不同)。

登记:INDEX.md §二 新增 04-113–128 共 16 行 + §四 追加 T09–T21 说明 + 机器摘要行刷新
(⛔ 未跑 docs-index-stats.py --write:该脚本会按 \r\n 归一化全文件行尾,故改为字节级单行替换);
README.md 追加 1 条入仓指针;docs-manifest.json 复跑 scripts/docs-manifest.py 刷新。

验收:工作区根 47 份 .md —— 同名已入仓 8 / 本次内容一致 29 / 已知改名映射 10 / 未入仓 0;
git status 待提交清单只含本次新增与登记 3 件(未涉 src/ 与 relay 代码面)。
2026-09-17 18:24:19 +08:00

207 lines
12 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.
# 项目代码 · 分层范式与迭代风险评估(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/*`)正好是「契约前置」的第一个样板。