📐
概念 #進化的アーキテクチャ #Fitness Functions #CI/CD #アーキテクチャ 📚 進化的アーキテクチャ

適応度関数(Fitness Functions):アーキテクチャ特性を自動検証する

Fitness Function の定義・分類マトリクス・実装例を整理する。テストが機能を守るのに対し、適応度関数はアーキテクチャの特性を守る

Fitness Function とは

最適化理論では Fitness Function は「解の良さを測る指標」を意味する。進化的アーキテクチャではこれを借用し、アーキテクチャが守るべき特性(パフォーマンス・結合度・セキュリティなど)を継続的に検証する仕組みを指す。

テストとの違いは検証対象だ。

テストFitness Function
守るもの機能の正しさ(ビジネスロジック)アーキテクチャの特性(構造・品質属性)
実装場所テストファイル(unit/integration)CI パイプライン・インフラ監視
「注文の合計金額が正しいか」「循環依存が存在しないか」「P99 が 200ms 以内か」

分類マトリクス

Fitness Function は 3 軸で分類される。

軸1: 原子的 vs 全体的

原子的(Atomic)全体的(Holistic)
意味単一のアーキテクチャ特性を検証複数の特性を組み合わせて検証
循環依存の検出レイテンシ × エラーレート × スループットの複合条件

軸2: 自動 vs 手動

自動(Automated)手動(Manual)
意味CI/CD で継続的に実行レビュー・監査など人間が実施
ArchUnit での依存関係チェックアクセシビリティ監査・セキュリティレビュー

軸3: 一時的 vs 継続的

一時的(Temporal)継続的(Continual)
意味移行期など特定期間だけ有効恒久的に CI で動かし続ける
データ移行中の整合性チェックレイテンシ閾値・依存制約

実例(カテゴリ別)

原子的 × 自動 × 継続的:循環依存検出

// ArchUnit(TypeScript 版ツールのイメージ)
// src/domain が src/infrastructure を import していないことを検証する
// ビルド時に実行し、違反したら CI を落とす
import { checkDependencies } from 'arch-unit-ts';

checkDependencies({
  noCircularDependencies: true,
  layerRules: [
    // domain は infrastructure に依存してはいけない
    { from: 'src/domain', to: 'src/infrastructure', allowed: false },
    { from: 'src/usecase', to: 'src/domain', allowed: true },
  ],
});

全体的 × 自動 × 継続的:レイテンシ P99 閾値(k6)

// k6 ロードテストを CI で定期実行し、P99 が 200ms を超えたら失敗とする
import { check } from 'k6';
import http from 'k6/http';

export const options = {
  thresholds: {
    // Fitness Function: P99 が 200ms 以内であること
    http_req_duration: ['p(99)<200'],
    // Fitness Function: エラー率が 1% 以内であること
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  const res = http.get('https://api.example.com/orders');
  check(res, { 'status is 200': (r) => r.status === 200 });
}

全体的 × 手動 × 継続的:アクセシビリティ監査

自動化が難しい特性は手動で定期実施する。WCAG 準拠チェックをリリースサイクルごとにチェックリスト化し、記録を残す。

原子的 × 自動 × 一時的:移行期のデータ整合性チェック

// 旧カラムと新カラムの値が一致していることを確認する(Expand-Contractの移行期だけ有効)
// 移行完了後にこのチェックを削除することを前提に書く
async function validateMigrationConsistency(db: Database): Promise<void> {
  const inconsistent = await db.query(`
    SELECT id FROM users
    WHERE old_email IS NOT NULL
      AND new_email IS NOT NULL
      AND old_email != new_email
    LIMIT 10
  `);
  if (inconsistent.rows.length > 0) {
    throw new Error(`データ不整合が検出されました: ${inconsistent.rows.length} 件`);
  }
}

CI/CD への組み込みイメージ

# .github/workflows/fitness-functions.yml
name: Fitness Functions

on: [push, pull_request]

jobs:
  architecture-constraints:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pnpm install
      # 原子的 × 自動 × 継続的
      - run: pnpm arch:check        # 循環依存・レイヤー制約

  performance-baseline:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # 全体的 × 自動 × 継続的(main ブランチのみ)
      - run: k6 run fitness/perf-threshold.js
        if: github.ref == 'refs/heads/main'

TDD との使い分け

テスト駆動開発と進化的アーキテクチャの Fitness Function は補完的だ。

  TDD          → 「注文の合計金額が正しいか」      (機能の正しさ)
  Fitness Fn   → 「注文サービスが決済サービスを直接呼んでいないか」(構造の正しさ)

機能テストが「何をするか」を守り、Fitness Function が「どのように作られているか」を守る。両方を CI で走らせることで、機能もアーキテクチャも継続的に保護できる。

関連概念

出典・参考文献

  • Neal Ford et al., Building Evolutionary Architectures, Chapter 2 “Fitness Functions”
  1. 1. 🧬進化的アーキテクチャとは:定義・誕生背景・3原則
  2. 2. 📐適応度関数(Fitness Functions):アーキテクチャ特性を自動検証する

出典: 進化的アーキテクチャ(O'Reilly / Neal Ford, Rebecca Parsons, Patrick Kua)