Skip to content

アーキテクチャルール検証

dependency-cruiserによる自動化されたアーキテクチャ整合性チェック

日付: 2025-08-12

ステータス

2025-08-12 承認済み

関連する決定: - 007-依存性注入とアーキテクチャリファクタリング.md の実装品質保証として連携

コンテキスト

Iteration 4完了後のふりかえりで、「予防的品質管理」の重要性が明確になった:

現在の課題

  • アーキテクチャ違反の手動チェック: レビュー時の人的ミス
  • レイヤー間依存関係: 逆向き依存の検出困難
  • 循環依存: インポートチェーン複雑化による見逃し
  • 技術的負債の蓄積: ルール違反の早期発見不足

Clean Architectureのルール検証要件

  1. 外から内への単方向依存: Presentation → Application → Domain
  2. ドメイン層の独立性: 外部ライブラリへの依存禁止
  3. 循環依存の禁止: モジュール間の循環インポート防止
  4. 許可されたインポートパス: 定義済みルール以外の依存関係禁止

決定

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

実装ガイドライン

ルール違反時の対応フロー

  1. CI失敗時:

    npm run arch:check    # 違反詳細確認
    npm run arch:graph    # 依存関係可視化
    

  2. 修正方針:

  3. レイヤー境界違反 → インポートパス修正
  4. 循環依存 → インターフェース導入・依存関係逆転
  5. 禁止パターン → アーキテクチャ設計見直し

  6. 例外処理:

    // 正当な理由がある場合のルール除外
    {
      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
  • 成功条件: アーキテクチャ違反の自動検出、開発効率向上