建立前端依賴治理規範文件,.dependency-cruiser.cjs 已參照此 ADR。 內容包含: - Layer Model 四層架構定義 - Feature Isolation 規則說明 - CI 整合配置 (pnpm dep-check) - Severity 分級策略 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
219 lines
5.7 KiB
Markdown
219 lines
5.7 KiB
Markdown
# ADR-014: Dependency Governance
|
||
|
||
> **狀態**: 已採用
|
||
> **日期**: 2026-03-26
|
||
> **決策者**: CTO
|
||
|
||
## 背景
|
||
|
||
AWOOOI 前端專案 (`apps/web`) 隨著功能擴展,元件間的依賴關係日趨複雜:
|
||
|
||
1. **Feature 間相互引用**: agent/approval/incident/dashboard 元件互相 import,導致修改一處牽動多處
|
||
2. **分層架構混亂**: ui 層引用 feature 層、hooks 引用 components,違反單向依賴原則
|
||
3. **循環依賴**: A → B → C → A 造成難以追蹤的 bundle 問題
|
||
4. **缺乏自動化檢查**: 僅靠 Code Review 把關,容易遺漏
|
||
|
||
這些問題導致:
|
||
- 重構風險高,修改一個元件可能影響多個頁面
|
||
- 測試困難,無法隔離測試單一 feature
|
||
- 新人學習曲線陡峭
|
||
|
||
---
|
||
|
||
## 決策
|
||
|
||
採用 **dependency-cruiser** 工具,在 CI 階段自動檢查依賴規則。
|
||
|
||
---
|
||
|
||
## 理由
|
||
|
||
### 為什麼選 dependency-cruiser
|
||
|
||
1. **成熟穩定**: npm 週下載量 100K+,持續維護
|
||
2. **規則靈活**: 支援 regex、path match、severity 分級
|
||
3. **CI 友善**: 非零退出碼可阻擋違規 PR
|
||
4. **視覺化**: 可產出依賴圖 (dot/svg)
|
||
5. **TypeScript 原生支援**: 無需額外配置
|
||
|
||
### 考慮過的替代方案
|
||
|
||
| 方案 | 優點 | 缺點 |
|
||
|------|------|------|
|
||
| ESLint import/no-restricted-paths | 現有工具鏈 | 規則表達力有限 |
|
||
| NX enforce-module-boundaries | 強大 | 需導入 NX 生態系 |
|
||
| 手動 Code Review | 零成本 | 不可靠、不可擴展 |
|
||
|
||
---
|
||
|
||
## 技術實作
|
||
|
||
### 1. Layer Model (四層架構)
|
||
|
||
```
|
||
Layer 0: Pages (app/)
|
||
├── 可引用: 所有層
|
||
├── 職責: 路由、佈局、頁面組裝
|
||
│
|
||
Layer 1: Features (components/agent, approval, incident, dashboard)
|
||
├── 可引用: Layer 2, Layer 3
|
||
├── 禁止: 互相引用 (agent ↔ approval ↔ incident ↔ dashboard)
|
||
├── 職責: 業務邏輯封裝
|
||
│
|
||
Layer 2: Shared (components/shared, layout)
|
||
├── 可引用: Layer 3
|
||
├── 禁止: 引用 Layer 1 (下行依賴)
|
||
├── 職責: 跨 Feature 共用元件
|
||
│
|
||
Layer 3: Primitives (components/ui, lib/, stores/, hooks/)
|
||
├── 可引用: 僅外部套件
|
||
├── 禁止: 引用 components (保持純淨)
|
||
├── 職責: 基礎工具、原子元件
|
||
```
|
||
|
||
### 2. 依賴方向 (單向流動)
|
||
|
||
```
|
||
Pages (L0) → Features (L1) → Shared (L2) → Primitives (L3)
|
||
↓ 禁止 ↓ 禁止
|
||
互相引用 反向引用
|
||
```
|
||
|
||
### 3. Feature Isolation 規則
|
||
|
||
每個 Feature 為獨立模組,禁止橫向依賴:
|
||
|
||
```
|
||
❌ agent/xxx.tsx → import from 'approval/yyy'
|
||
❌ approval/xxx.tsx → import from 'incident/yyy'
|
||
❌ incident/xxx.tsx → import from 'dashboard/yyy'
|
||
|
||
✅ agent/xxx.tsx → import from 'shared/yyy'
|
||
✅ approval/xxx.tsx → import from 'ui/zzz'
|
||
```
|
||
|
||
### 4. 配置檔案 (.dependency-cruiser.cjs)
|
||
|
||
```javascript
|
||
module.exports = {
|
||
forbidden: [
|
||
// Feature Isolation (L1 禁止互相引用)
|
||
{
|
||
name: "feature-isolation-agent",
|
||
severity: "error",
|
||
from: { path: "apps/web/src/components/agent" },
|
||
to: { path: "apps/web/src/components/(approval|incident|dashboard)" }
|
||
},
|
||
// ... 其他 feature 同理
|
||
|
||
// Shared 禁止下行引用 (L2 → L1)
|
||
{
|
||
name: "shared-no-feature-import",
|
||
severity: "error",
|
||
from: { path: "apps/web/src/components/shared" },
|
||
to: { path: "apps/web/src/components/(agent|approval|incident|dashboard)" }
|
||
},
|
||
|
||
// Primitives 禁止引用 components (L3 保持純淨)
|
||
{
|
||
name: "hooks-no-component-import",
|
||
severity: "warn",
|
||
from: { path: "apps/web/src/hooks" },
|
||
to: { path: "apps/web/src/components" }
|
||
},
|
||
|
||
// 禁止循環依賴
|
||
{
|
||
name: "no-circular",
|
||
severity: "error",
|
||
from: {},
|
||
to: { circular: true }
|
||
},
|
||
|
||
// Components 禁止反向引用 app 層
|
||
{
|
||
name: "components-no-app-import",
|
||
severity: "error",
|
||
from: { path: "apps/web/src/components" },
|
||
to: { path: "apps/web/src/app" }
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 5. CI 整合
|
||
|
||
**package.json:**
|
||
```json
|
||
{
|
||
"scripts": {
|
||
"dep-check": "depcruise apps/web/src --config .dependency-cruiser.cjs"
|
||
}
|
||
}
|
||
```
|
||
|
||
**ci.yaml:**
|
||
```yaml
|
||
- name: Dependency Check
|
||
run: pnpm dep-check
|
||
```
|
||
|
||
### 6. Severity 分級
|
||
|
||
| Level | 行為 | 使用場景 |
|
||
|-------|------|---------|
|
||
| `error` | CI 失敗 | Feature Isolation、循環依賴 |
|
||
| `warn` | 輸出警告 | L3 引用 components (過渡期) |
|
||
| `info` | 僅紀錄 | 監控用 |
|
||
|
||
---
|
||
|
||
## 執行方式
|
||
|
||
### Phase 1: 啟用 (本週)
|
||
|
||
- [x] `.dependency-cruiser.cjs` 配置完成
|
||
- [x] `pnpm dep-check` script 加入
|
||
- [x] CI workflow 加入 dep-check step
|
||
- [x] ADR-014 文件建立
|
||
|
||
### Phase 2: 修復違規 (下週)
|
||
|
||
- [ ] 修復現有 Feature Isolation 違規
|
||
- [ ] 修復 Shared → Feature 違規
|
||
- [ ] 警告轉為錯誤 (hooks/stores/lib)
|
||
|
||
### Phase 3: 持續維護
|
||
|
||
- [ ] 新 PR 自動檢查
|
||
- [ ] 季度產出依賴圖供架構審查
|
||
|
||
---
|
||
|
||
## 後果
|
||
|
||
### 優點
|
||
|
||
- **架構可維護**: 依賴方向明確,修改影響範圍可預測
|
||
- **自動化檢查**: CI 阻擋違規,不再依賴人工 Review
|
||
- **Feature 可獨立開發**: 各 Feature 團隊可平行作業
|
||
- **測試隔離**: 可針對單一 Feature 進行單元測試
|
||
|
||
### 缺點
|
||
|
||
- **初期修復成本**: 需花時間修復現有違規
|
||
- **學習成本**: 開發者需理解 Layer Model
|
||
|
||
### 風險
|
||
|
||
- **過度嚴格**: 某些合理的跨 Feature 需求可能被誤擋
|
||
- **緩解**: 使用 `severity: warn` 過渡期,必要時新增白名單
|
||
|
||
---
|
||
|
||
## 參考
|
||
|
||
- [dependency-cruiser 官方文檔](https://github.com/sverweij/dependency-cruiser)
|
||
- [Clean Architecture - Dependency Rule](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|
||
- [NX Module Boundaries](https://nx.dev/core-features/enforce-module-boundaries)
|