深度解析
为什么需要 CLI,而不只靠 linter
架构规则是图属性。单个文件看不到它们。
简短回答
ESLint、Biome 和 Oxlint 看的是单个文件(或字符串 import 启发式)。循环、入边谓词和提升(promotion)是整张图的属性。这需要单独的分析器——dma check。
编辑器里的 linter 仍然有用。但 CI 没有 CLI 就是架构防护栏上的洞。
linter 能看到什么
DMA 按文件作用域的规则:
- import 方向违反层规则(
layer-direction) - feature → feature(
feature-to-feature) - 绕过
public/的 import(public-api) - 带 re-export 的 barrel
index(no-barrel)
ESLint 通过 @derived-modular/boundaries 和路径解析精确做到这些。Biome 用字符串模式的启发式。
linter 看不到什么
| 规则 | 为什么需要图 |
|---|---|
no-cycle | 循环是跨多个文件的路径 A→B→C→A |
feature-has-inbound | 入边是在所有模块上统计的,不是单个文件里的一次 import |
service-no-inbound | 没有消费者的 service 是子图属性 |
dma doctor | 整个项目的演进信号 |
示例:循环 services/billing → services/cart → services/inventory → services/billing——每个文件里的一次 import 看起来都合法,而 no-cycle 只有看全图才能发现。或者 catalog 直接 import checkout——ESLint 能在文件里抓到 feature-to-feature,但入边谓词 feature-has-inbound 以及跨多个模块的链——只有 check 能处理。
DMA 的混合模型
Editor → linter(快,按文件)
CI → dma check(全图)
Locally → dma doctor(软信号)一个分析器(@derived-modular/cli)——单一事实来源。linter 是 @derived-modular/boundaries 上的薄适配层,不是第二套事实来源。
「我们配好了 ESLint——架构没问题了」在 CI 里跑过 dma check 之前都是错觉。
为什么不用一个巨型 ESLint 规则
理论上 ESLint 可以分析整个项目。实践中:
- linter 为按文件工作和按文件缓存优化;
- 每次保存全图扫描很贵;
- 循环和入边是另一类算法,不是单个模块的 AST。
DMA 刻意把图审计放进 CLI。linter 服务于 IDE 反馈循环。
覆盖矩阵
| 关注点 | ESLint | Biome | dma check |
|---|---|---|---|
layer-direction | ✓ | 启发式 | ✓ |
feature-to-feature | ✓ | 启发式 | ✓ |
public-api | ✓ | 启发式 | ✓ |
no-barrel | ✓ | 启发式 | ✓ |
no-cycle | — | — | ✓ |
| 入边谓词 | — | — | ✓ |
| doctor | — | — | ✓ |
实践建议
- 在编辑器里接入 ESLint(或 Biome / Oxlint)
- 在 CI 中加入
npx @derived-modular/cli check . - 每个 sprint 跑一次
npx @derived-modular/cli doctor .看增长信号