ひとことで言うと#
ビジネス要件の変化に合わせてアーキテクチャを安全に進化させ続けるための考え方。「フィットネス関数」と呼ばれる品質ガードレールを自動テストとして組み込み、アーキテクチャの健全性を常時モニタリングしながら段階的に変更を加える。
押さえておきたい用語#
- フィットネス関数(Fitness Function)
- アーキテクチャの特性(パフォーマンス、セキュリティ、結合度など)が基準を満たしているかを自動的に検証する仕組み。CIパイプラインに組み込んで継続的に監視する。
- ガイドされた進化(Guided Evolution)
- 無秩序な変更ではなく、フィットネス関数で品質を守りながら意図的に方向づけられた進化のこと。
- 増分変更(Incremental Change)
- アーキテクチャを一度に大きく変えるのではなく、小さな単位で少しずつ変更し、各ステップでフィットネス関数を通す手法。
- アーキテクチャ量子(Architecture Quantum)
- 独立してデプロイ可能な最小のアーキテクチャ単位。マイクロサービスにおける1つのサービスがこれに該当する。
- 構造的結合度(Structural Coupling)
- モジュール間の依存関係の度合い。進化的アーキテクチャでは結合度を低く保つことが変更容易性の前提条件になる。
進化的アーキテクチャの全体像#
こんな悩みに効く#
- リファクタリングするたびに予想外の箇所が壊れ、変更が怖くなっている
- 「いつか大規模に作り直す」と言い続けて3年経っている
- パフォーマンスやセキュリティの劣化に気づくのがいつもリリース後
- マイクロサービスに移行したいが、どこから手をつけていいかわからない
基本の使い方#
ビジネス要件から逆算して、アーキテクチャが満たすべき品質特性を洗い出す。
- パフォーマンス: レスポンスタイム、スループット
- スケーラビリティ: 同時接続数、データ量の上限
- セキュリティ: 脆弱性ゼロ、暗号化要件
- 結合度: モジュール間の依存の上限
- 全部を一度に定義する必要はない。最も重要な3〜5個から始める
各品質特性を検証する自動テストを作成し、CIパイプラインに組み込む。
- パフォーマンス: 主要APIのレスポンスタイムが200ms以下であることをテスト
- 結合度: ArchUnitやdeptryでモジュール間の循環依存がゼロであることを検証
- セキュリティ: 依存ライブラリの脆弱性スキャン(Snyk、Trivyなど)をCIで自動実行
- フィットネス関数は「合格/不合格」のバイナリで判定する。曖昧な基準は効果がない
大きな変更を一度にやるのではなく、小さな変更を頻繁に加える。
- Strangler Figパターンで旧コードを段階的に新コードに置き換える
- 1つの変更ごとにフィットネス関数を全て通す(CIで自動実行)
- 関数が失敗したら変更をリバートし、原因を調査してから再挑戦
- 変更の単位が小さいほど、失敗時の原因特定が容易になる
ビジネス要件の変化に合わせて、フィットネス関数自体もアップデートする。
- 新しい品質要件(例: GDPR対応)が発生したら新しい関数を追加
- 閾値の見直し(例: レスポンス200ms→150ms)を四半期ごとに実施
- 不要になった関数は削除し、メンテナンスコストを抑える
- フィットネス関数の変更もADRに記録する
具体例#
月間注文5万件のECサイト。PHPモノリスで運用していたが、新機能の追加に平均3週間かかり、デプロイのたびに全機能のリグレッションテストに8時間を要していた。
フィットネス関数の設定:
| 特性 | 関数 | 閾値 |
|---|---|---|
| レスポンスタイム | 商品一覧APIの95%ile | 300ms以下 |
| 結合度 | モジュール間循環依存数 | 0件 |
| デプロイ頻度 | 週あたりデプロイ回数 | 3回以上 |
| テストカバレッジ | コアドメインのカバレッジ | 80%以上 |
増分変更の実施:
- まず商品カタログサービスをモノリスから分離(Strangler Fig)
- フィットネス関数を全てパスすることを確認
- 次に注文サービスを分離
- 4か月間で5つのサービスを順次分離
各ステップでフィットネス関数が品質を担保した。2つ目のサービス分離時にレスポンスタイムが420msに劣化し関数が失敗。原因はサービス間通信のN+1問題で、バッチAPIを追加して解決した。
最終的に新機能の追加は3週間→5日に短縮。デプロイ頻度は週1回→週8回に向上。フィットネス関数がなければ「パフォーマンス劣化に気づかないまま移行を進めていた」とテックリードは振り返った。
ヘルスケアSaaS。HIPAA準拠が必要で、年1回のセキュリティ監査に毎回2か月の準備期間を要していた。監査のたびに「暗号化漏れ」「アクセスログの欠落」が発見され、緊急修正に追われていた。
導入したフィットネス関数:
- 暗号化関数: DBの全カラムのうち個人情報カラムが暗号化されていることを検証
- ログ関数: 全APIエンドポイントにアクセスログが記録されていることを検証
- 依存関数: CVSSスコア7以上の脆弱性を持つライブラリがゼロであることを検証
- 認証関数: 認証なしでアクセスできるエンドポイントがホワイトリスト以外にないことを検証
これらをCIパイプラインに組み込み、PR単位で自動実行。新しいエンドポイントを追加したPRで「ログ関数」が失敗し、開発者がログ記録を追加してから再提出する、という流れが日常化した。
導入1年後の監査では、準備期間が2か月→2週間に短縮。指摘事項はゼロになり、監査法人から「継続的にセキュリティを管理している模範的なケース」と評価された。
官公庁向け業務システム(Java 8 + Struts)。ユーザー数3,000名で、年度末のピーク時にレスポンスが5秒以上になる画面が12個あった。全面リプレースは予算的に不可能で、段階的な改善が求められた。
フィットネス関数:
| 特性 | 閾値 | 測定方法 |
|---|---|---|
| 画面レスポンス | 95%ileで3秒以下 | Gatlingで負荷テスト |
| メモリ使用量 | ヒープ使用率80%以下 | Prometheus監視 |
| DB接続数 | コネクションプール使用率70%以下 | HikariCPメトリクス |
増分変更の計画:
- Phase 1: 最も遅い3画面のSQLチューニング(2週間)
- Phase 2: セッション管理をインメモリからRedisに移行(3週間)
- Phase 3: 重い集計処理を非同期バッチに切り出し(4週間)
各Phase完了時にGatlingで負荷テストを実行し、フィットネス関数を通した。Phase 2でメモリ使用率が**85%**に上昇しフィットネス関数が失敗。Redisクライアントの接続プール設定を修正して解決。
3か月後、5秒超の画面は12個→0個に。年度末ピーク時でも95%ileが2.1秒に収まった。全面リプレースなしに、段階的改善で品質を回復した。
やりがちな失敗パターン#
- フィットネス関数を作りすぎる — 最初から20個も作ると、メンテナンスコストが高くなり形骸化する。最も重要な3〜5個から始めて、必要に応じて追加する
- 閾値を曖昧に設定する — 「パフォーマンスが良いこと」ではなく「95%ileで200ms以下」のように定量的に定義しなければ、合格/不合格の判定ができない
- フィットネス関数の失敗を無視する — CIが赤くなっても「後で直す」とマージしてしまうと、ガードレールの意味がなくなる。失敗したら必ず修正してからマージする
- 大きな変更を一度にやろうとする — 増分変更の原則を無視して大規模リファクタリングを一気にやると、フィットネス関数が複数同時に失敗し、原因特定が困難になる
まとめ#
進化的アーキテクチャは、「品質を守るガードレール(フィットネス関数)」と「小さく頻繁な変更(増分変更)」の2つで成り立つ。大事なのはアーキテクチャを固定することではなく、変化を前提にしつつ品質を劣化させない仕組みを持つこと。フィットネス関数をCIに組み込めば、パフォーマンスやセキュリティの劣化にリリース前に気づける。アーキテクチャは一度決めたら終わりではなく、育て続けるものである。