アーキテクチャルール検証¶
dependency-cruiserによる自動化されたアーキテクチャ整合性チェック
日付: 2025-08-12
ステータス¶
2025-08-12 承認済み
関連する決定: - 007-依存性注入とアーキテクチャリファクタリング.md の実装品質保証として連携
コンテキスト¶
Iteration 4完了後のふりかえりで、「予防的品質管理」の重要性が明確になった:
現在の課題¶
- アーキテクチャ違反の手動チェック: レビュー時の人的ミス
- レイヤー間依存関係: 逆向き依存の検出困難
- 循環依存: インポートチェーン複雑化による見逃し
- 技術的負債の蓄積: ルール違反の早期発見不足
Clean Architectureのルール検証要件¶
- 外から内への単方向依存: Presentation → Application → Domain
- ドメイン層の独立性: 外部ライブラリへの依存禁止
- 循環依存の禁止: モジュール間の循環インポート防止
- 許可されたインポートパス: 定義済みルール以外の依存関係禁止
決定¶
dependency-cruiser によるアーキテクチャルール検証を導入する
アーキテクチャ検証ルール¶
1. レイヤー間依存関係ルール¶
// Presentation層は Application層のみに依存可能
{
name: 'presentation-to-application-only',
from: { path: '^src/components' },
to: {
path: '^src/(?!application|domain|services|infrastructure)',
pathNot: '^src/(application|domain|services|infrastructure)'
},
severity: 'error'
}
// Domain層は外部依存禁止(React、外部ライブラリ禁止)
{
name: 'domain-independence',
from: { path: '^src/domain' },
to: {
path: 'node_modules',
pathNot: '^src/domain'
},
severity: 'error'
}
2. 循環依存検出ルール¶
{
name: 'no-circular-dependencies',
severity: 'error',
from: {},
to: {
circular: true
}
}
3. 禁止されたインポートパターン¶
{
name: 'no-infrastructure-to-domain',
from: { path: '^src/infrastructure' },
to: { path: '^src/domain' },
severity: 'error'
}
理由¶
dependency-cruiser採用理由:
- 自動化: CI/CDでのアーキテクチャルール検証
- 可視化: 依存関係グラフの生成とレポート
- カスタマイズ性: プロジェクト固有ルールの柔軟な定義
- TypeScript完全対応: 型情報考慮の高精度解析
他の選択肢を除外した理由:
- ESLint import/no-cycle: 循環依存のみ、アーキテクチャルールに不十分
- madge: 可視化主体、詳細なルール定義困難
- 手動レビュー: スケールしない、人的ミス発生
影響¶
ポジティブな影響¶
- 品質保証: アーキテクチャ違反の自動検出・防止
- 開発効率: 早期フィードバックによるデバッグコスト削減
- 保守性: 長期的なアーキテクチャ整合性の維持
- 新人教育: ルール学習の自動化、アーキテクチャ理解促進
ネガティブな影響¶
- 学習コスト: dependency-cruiser設定ファイルの理解
- CI時間: ビルド時間への若干の影響
- 初期設定: 既存コードのルール適合確認
軽減策¶
- 段階的導入: 警告レベルから開始、段階的にエラー化
- ドキュメント整備: ルール解説とトラブルシューティング
- キャッシュ活用: CI/CDでの差分チェック最適化
技術仕様¶
インストールと設定¶
# dependency-cruiser インストール
npm install --save-dev dependency-cruiser
# 設定ファイル生成
npx depcruise --init
設定ファイル (.dependency-cruiser.js)¶
module.exports = {
forbidden: [
// Clean Architecture: レイヤー間依存関係
{
name: 'presentation-layer-boundaries',
comment: 'プレゼンテーション層はアプリケーション層・サービス層のみ参照可能',
severity: 'error',
from: { path: '^src/components' },
to: {
path: '^src/(?!application|services|utils)',
pathNot: '^src/(application|services|utils|components)'
}
},
{
name: 'application-layer-boundaries',
comment: 'アプリケーション層はドメイン層・サービス層のみ参照可能',
severity: 'error',
from: { path: '^src/application' },
to: {
path: '^src/(?!domain|services|utils)',
pathNot: '^src/(domain|services|utils|application)'
}
},
{
name: 'domain-independence',
comment: 'ドメイン層は外部ライブラリに依存禁止',
severity: 'error',
from: { path: '^src/domain' },
to: {
path: 'node_modules',
pathNot: '^(typescript|@types)'
}
},
{
name: 'service-to-domain-only',
comment: 'サービス層はドメイン層・インフラストラクチャ層のみ参照可能',
severity: 'error',
from: { path: '^src/services' },
to: {
path: '^src/(?!domain|infrastructure|utils)',
pathNot: '^src/(domain|infrastructure|utils|services)'
}
},
// 循環依存の禁止
{
name: 'no-circular',
comment: '循環依存を禁止',
severity: 'error',
from: {},
to: { circular: true }
},
// 特定パターンの禁止
{
name: 'no-infrastructure-to-domain',
comment: 'インフラストラクチャ層からドメイン層への直接依存禁止',
severity: 'error',
from: { path: '^src/infrastructure' },
to: { path: '^src/domain' }
}
],
allowed: [
// TypeScript型定義は除外
{
from: { path: '^src' },
to: { path: '^src/types' }
}
],
options: {
doNotFollow: {
path: 'node_modules',
},
exclude: {
path: '\\.(test|spec)\\.(js|ts)$'
},
tsPreCompilationDeps: true,
tsConfig: {
fileName: './tsconfig.json'
},
enhancedResolveOptions: {
exportsFields: ["exports"],
conditionNames: ["import", "require", "node", "default"]
},
reporterOptions: {
dot: {
collapsePattern: 'node_modules/([^/]+)'
}
}
}
}
NPMスクリプト追加¶
{
"scripts": {
"arch:check": "depcruise src --validate",
"arch:graph": "depcruise --output-type dot src | dot -T svg > architecture-graph.svg",
"arch:report": "depcruise --output-type html src > dependency-report.html"
}
}
CI/CD統合¶
# GitHub Actions (.github/workflows/quality.yml)
- name: Architecture Rule Check
run: |
npm run arch:check
- name: Generate Architecture Graph
if: failure()
run: |
npm run arch:graph
npm run arch:report
- name: Upload Architecture Reports
if: failure()
uses: actions/upload-artifact@v3
with:
name: architecture-violations
path: |
architecture-graph.svg
dependency-report.html
実装ガイドライン¶
ルール違反時の対応フロー¶
-
CI失敗時:
npm run arch:check # 違反詳細確認 npm run arch:graph # 依存関係可視化
-
修正方針:
- レイヤー境界違反 → インポートパス修正
- 循環依存 → インターフェース導入・依存関係逆転
-
禁止パターン → アーキテクチャ設計見直し
-
例外処理:
// 正当な理由がある場合のルール除外 { from: { path: 'src/legacy-module' }, to: { path: 'src/domain' }, comment: 'レガシーモジュール: 段階的リファクタリング予定' }
段階的導入計画¶
Phase 1: 基本ルール (警告レベル)¶
- 循環依存検出
- 明らかなアーキテクチャ違反
Phase 2: Clean Architecture準拠 (エラーレベル)¶
- レイヤー間依存関係の厳格化
- ドメイン層独立性の徹底
Phase 3: 高度なルール¶
- モジュール結合度の計測
- 複雑度ベースの制限
品質指標¶
アーキテクチャ健全性メトリクス¶
- ルール違反数: 0件維持
- 循環依存数: 0件維持
- レイヤー境界違反: 0件維持
- ドメイン独立性: 100%維持
CI/CD統合結果¶
- 検証実行時間: <30秒目標
- アーキテクチャチェック成功率: 100%維持
- 違反早期検出: PRレベルでの検出
運用ガイドライン¶
定期メンテナンス¶
- 月次アーキテクチャレビュー: ルール適合性確認
- 四半期ルール見直し: プロジェクト進化に応じた調整
- 依存関係グラフ更新: ドキュメントとの同期
チーム運用¶
- 新規開発者オンボーディング: アーキテクチャルール教育
- コードレビュー項目: dependency-cruiserレポート確認
- リファクタリング計画: ルール違反解消の優先順位
コンプライアンス¶
この決定の遵守は以下により確認:
.dependency-cruiser.js
設定ファイルの存在と適切な設定- CI/CDでの
arch:check
実行とパス条件 - アーキテクチャ違反 0件の維持
- 依存関係グラフの定期生成と確認
備考¶
- 決定者: 開発チーム
- 影響範囲: アーキテクチャ品質管理全体
- 実装期間: Phase 4 運用フェーズで導入
- レビュー予定: 導入後1ヶ月、その後四半期毎
- 前提条件: 007-ADRで構築されたClean Architecture
- 成功条件: アーキテクチャ違反の自動検出、開発効率向上