ソフトウェアアーキテクチャを設計・評価するときの原則、判断基準、実践のポイントをまとめたガイドです。特定の技術スタックには依存しない内容にしています。


目次

  1. アーキテクチャ設計の目的
  2. 基本原則
  3. 品質特性(非機能要件)を起点にする
  4. 境界とモジュール分割
  5. 代表的なアーキテクチャスタイルと選び方
  6. データ設計
  7. 通信と統合
  8. 信頼性・回復性
  9. スケーラビリティとパフォーマンス
  10. セキュリティ
  11. 可観測性(Observability)
  12. デプロイと運用
  13. 意思決定の記録と進化
  14. よくあるアンチパターン
  15. 設計レビュー チェックリスト

1. アーキテクチャ設計の目的

アーキテクチャとは「後から変えにくい重要な決定の集合」です。目的は次の3つに集約されます。

  • ビジネス要件と品質要件を満たすこと:機能だけでなく、性能・可用性・保守性などを満たす
  • 変更コストを低く保つこと:将来の変更が局所的に済む構造にする
  • チームが並行して開発できること:組織構造とシステム構造を整合させる

良いアーキテクチャは「決定を遅らせられる」アーキテクチャである。今決めなくてよいことを、今決めないで済む構造を目指す。


2. 基本原則

2.1 シンプルさを優先する(KISS / YAGNI)

  • 現時点で必要な複雑さだけを導入する
  • 「将来必要になるかもしれない」拡張ポイントは、根拠がない限り作らない
  • 複雑さを導入するときは、それが解決する具体的な問題を説明できるようにする

2.2 関心の分離(Separation of Concerns)

  • UI、ビジネスロジック、データアクセス、外部連携を分離する
  • 1つのコンポーネントが変更される理由は1つに絞る(単一責任の原則)

2.3 高凝集・疎結合

  • 凝集度:一緒に変わるものは一緒に置く
  • 結合度:別々に変わるものは依存を最小化し、インターフェースを介して接続する
  • 依存の方向を意識する:安定したもの(ドメイン)に向かって依存させる(依存性逆転の原則)

2.4 明示的なインターフェース

  • コンポーネント間の契約(API、イベントスキーマ)を明文化する
  • 内部実装を隠蔽し、外部に公開する面を小さく保つ

2.5 トレードオフを意識する

  • すべての品質特性を最大化することはできない(例:一貫性 vs 可用性、柔軟性 vs 単純さ)
  • 何を優先し、何を犠牲にしたかを明示的に記録する

3. 品質特性(非機能要件)を起点にする

アーキテクチャを決めるのは機能要件よりも品質特性です。設計の初期段階で、優先すべき品質特性を特定し、測定可能な形で定義します。

品質特性定義の例(測定可能な形)
性能95パーセンタイルのレスポンスタイム 300ms 以内
可用性月間稼働率 99.9%(許容停止時間 約43分/月)
スケーラビリティピーク時 10倍のトラフィックに水平スケールで対応
保守性新機能の追加が既存モジュール1〜2個の変更で完結する
セキュリティ個人情報は保存時・通信時ともに暗号化
回復性RTO 1時間、RPO 5分
コスト月額インフラ費用を一定額以内に収める

実践のポイント

  • 品質特性は「優先順位付きの上位3〜5個」に絞る
  • 品質特性シナリオ(刺激 → 応答 → 測定値)の形で書くと評価しやすい
    • 例:「ピーク時に注文APIへ毎秒1,000リクエストが来たとき、95%を500ms以内に処理する」

4. 境界とモジュール分割

4.1 ドメインに沿って分割する

  • 技術レイヤー(Controller / Service / Repository)だけでなく、ビジネスの境界で分割する
  • ドメイン駆動設計(DDD)の 境界づけられたコンテキスト(Bounded Context) を活用する
  • 同じ言葉が異なる意味を持つ場所が境界の目印(例:「顧客」が営業と請求で異なる意味を持つ)

4.2 コンウェイの法則を味方につける

  • システムの構造は組織のコミュニケーション構造を反映する
  • チーム境界とモジュール境界を一致させると、調整コストが下がる
  • 必要に応じて、望むアーキテクチャに合わせてチーム構成を変える(逆コンウェイ戦略)

4.3 境界の健全性を保つ

  • モジュール間の依存関係を可視化し、循環依存を禁止する
  • 依存ルールを静的解析やアーキテクチャテストで自動検証する
  • 他モジュールのデータベースへの直接アクセスを禁止する

5. 代表的なアーキテクチャスタイルと選び方

スタイル向いている状況注意点
モジュラーモノリス初期フェーズ、小〜中規模チーム、ドメイン理解が途上モジュール境界を守る規律が必要
レイヤードアーキテクチャ業務アプリ全般、シンプルなCRUD中心ドメインロジックが層をまたいで散らばりやすい
クリーン / ヘキサゴナルドメインロジックが複雑、テスト容易性を重視小規模では過剰になりがち
マイクロサービス大規模組織、独立デプロイ・独立スケールが必須分散システムの複雑さ(ネットワーク、整合性、運用)が大きい
イベント駆動非同期処理、システム間の疎結合、リアルタイム連携処理の流れが追いにくい、結果整合性への対応が必要
サーバーレス断続的な負荷、イベント処理、運用負荷を下げたいコールドスタート、ベンダーロックイン、ローカル開発の難しさ

選び方の指針

  • 迷ったらモジュラーモノリスから始める。境界が安定してから必要な部分だけ分離する
  • マイクロサービスは「技術的な理由」より「組織的な理由(チームの独立性)」で採用を判断する
  • スタイルは混在してよい。システム全体で1つに統一する必要はない

6. データ設計

6.1 データの所有権を明確にする

  • 各データには 唯一の所有者(サービス/モジュール) を定める
  • 他のコンポーネントは所有者のAPIやイベントを通じてデータを取得する
  • 共有データベースは強い結合を生むため避ける

6.2 整合性モデルを選ぶ

  • 強い整合性が必要な範囲(例:残高、在庫の引当)は小さく保つ
  • それ以外は 結果整合性 を受け入れ、補償処理(Sagaパターン等)で対応する
  • トランザクション境界 = 集約(Aggregate)の境界を基本とする

6.3 ストレージの選択

  • 用途に応じて適材適所で選ぶ(RDB、ドキュメントDB、KVS、検索エンジン、時系列DBなど)
  • ただし種類を増やすほど運用コストが増える。まずは汎用的なRDBで足りるかを検討する

6.4 スキーマの進化

  • 後方互換性を保ったスキーマ変更を基本とする(追加は容易、削除・変更は段階的に)
  • Expand → Migrate → Contract の手順で無停止移行を行う

7. 通信と統合

7.1 同期 vs 非同期

同期(REST / gRPC 等)非同期(メッセージキュー / イベント)
長所シンプル、即時に結果が得られる疎結合、負荷平準化、障害の局所化
短所呼び出し先の障害が連鎖する処理の追跡が難しい、結果整合性
向く用途問い合わせ、即時応答が必要な操作通知、バックグラウンド処理、システム間連携

7.2 API設計

  • APIは公開した時点で契約になる。バージョニング方針を最初に決める
  • リソース指向で一貫した命名・エラー形式を採用する
  • 冪等性を確保する(冪等キーの導入、PUTの活用など)
  • ページング、フィルタリング、レート制限を最初から考慮する

7.3 イベント設計

  • イベントは「起きた事実」を過去形で表現する(例:OrderPlaced)
  • イベントスキーマを管理し、互換性を保つ
  • 少なくとも1回配信(at-least-once)を前提に、受信側を冪等にする

8. 信頼性・回復性

「障害は必ず起きる」を前提に設計する。

  • タイムアウト:すべての外部呼び出しにタイムアウトを設定する
  • リトライ:指数バックオフ+ジッターで実施し、冪等な操作に限定する
  • サーキットブレーカー:障害中の依存先への呼び出しを遮断し、連鎖障害を防ぐ
  • バルクヘッド:リソース(スレッド、接続プール)を分離し、障害の影響範囲を限定する
  • グレースフルデグラデーション:一部機能が使えなくても主要機能は提供し続ける
  • 単一障害点(SPOF)の排除:冗長化、複数アベイラビリティゾーンへの分散
  • バックアップと復旧訓練:バックアップは「復元できること」を定期的に確認して初めて意味がある
  • カオスエンジニアリング:計画的に障害を注入し、回復性を検証する

9. スケーラビリティとパフォーマンス

  • ステートレス化:アプリケーションサーバーに状態を持たせず、水平スケールを容易にする
  • キャッシュ:読み取りが多いデータはキャッシュする。ただし無効化戦略を必ずセットで設計する
  • 非同期化:重い処理はキューに逃がし、応答時間を短く保つ
  • データの分割:読み取りレプリカ、シャーディング、パーティショニング
  • ボトルネックの計測:推測ではなく、負荷試験とプロファイリングで特定する
  • 早すぎる最適化を避ける:まず正しく動く構造を作り、計測に基づいて最適化する

10. セキュリティ

  • セキュリティ・バイ・デザイン:後付けではなく設計段階から組み込む
  • 多層防御(Defense in Depth):ネットワーク、アプリケーション、データの各層で防御する
  • 最小権限の原則:ユーザー、サービス、インフラそれぞれに必要最小限の権限のみ付与する
  • ゼロトラスト:内部ネットワークであっても通信を認証・認可・暗号化する
  • 機密情報の管理:シークレットはコードや設定ファイルに埋め込まず、専用の管理基盤を使う
  • 入力の検証:すべての外部入力を信頼せず、境界で検証する
  • 脅威モデリング:STRIDE等の手法で、設計段階に脅威を洗い出す
  • 依存関係の管理:ライブラリの脆弱性を継続的にスキャンし、更新する
  • 監査ログ:誰が、いつ、何をしたかを追跡できるようにする

11. 可観測性(Observability)

システムの内部状態を外部から把握できるようにする。

要素内容ポイント
ログ個別イベントの記録構造化ログ(JSON等)、相関IDの付与、個人情報のマスキング
メトリクス数値の時系列データRED(Rate / Errors / Duration)、USE(Utilization / Saturation / Errors)
トレースリクエストの経路追跡分散トレーシングでサービス間の遅延を可視化

実践のポイント

  • SLI / SLO を定義し、エラーバジェットで信頼性と開発速度のバランスを取る
  • アラートは「ユーザー影響のある症状」に対して設定し、ノイズを減らす
  • ダッシュボードはサービスごとに標準化する

12. デプロイと運用

  • Infrastructure as Code:インフラ構成をコードで管理し、再現性を確保する
  • CI/CD:ビルド、テスト、デプロイを自動化し、小さく頻繁にリリースする
  • 段階的リリース:カナリアリリース、ブルーグリーンデプロイ、フィーチャーフラグでリスクを抑える
  • ロールバック容易性:すべてのデプロイは即座に戻せるようにする
  • 環境の一貫性:開発・検証・本番の差異を最小化する(コンテナの活用など)
  • 運用を設計に含める:誰がどう運用するかを設計段階で決め、Runbookを整備する

13. 意思決定の記録と進化

13.1 ADR(Architecture Decision Record)

重要な設計判断は ADR として記録する。

# ADR-XXX: [決定のタイトル]
 
## ステータス
提案 / 承認 / 廃止 / 置き換え(ADR-YYY)
 
## コンテキスト
なぜこの決定が必要なのか。前提条件、制約、課題。
 
## 決定
何を決めたのか。
 
## 検討した選択肢
- 選択肢A:長所 / 短所
- 選択肢B:長所 / 短所
 
## 結果
この決定によって何が良くなり、何を受け入れるのか(トレードオフ)。

13.2 アーキテクチャの可視化

  • C4モデル(Context → Container → Component → Code)で抽象度ごとに図を描く
  • 図はコードと同じリポジトリで管理し(Mermaid、PlantUML 等)、陳腐化を防ぐ

13.3 進化的アーキテクチャ

  • アーキテクチャは一度決めて終わりではなく、継続的に見直す
  • 適応度関数(Fitness Function):守るべき性質(依存ルール、性能閾値など)を自動テストで検証する
  • 大きな移行は ストラングラーフィグパターン で段階的に置き換える

14. よくあるアンチパターン

アンチパターン症状対策
分散モノリスサービスは分かれているが、同時にデプロイしないと動かない境界の見直し、同期依存の削減、データ所有権の明確化
ビッグボールオブマッド構造がなく、どこを変えても影響が予測できないモジュール境界の導入、依存ルールの自動検証
共有データベース複数サービスが同じテーブルを直接読み書きするデータ所有者を定め、API/イベント経由に移行
過剰設計使われない抽象化層や拡張ポイントが大量にあるYAGNIの徹底、必要になってから導入
黄金のハンマーどんな課題にも同じ技術・パターンを適用する要件と品質特性から選択肢を比較検討する
象牙の塔のアーキテクト設計者が実装から離れ、現実と乖離した設計になるアーキテクトも実装・レビューに参加する
ドキュメントなき決定「なぜこうなっているのか」誰も分からないADRで判断理由を記録する
チャッティなサービス間通信1リクエストで多数のサービス呼び出しが連鎖する境界の再設計、データの複製、集約API

15. 設計レビュー チェックリスト

要件・前提

  • 優先すべき品質特性が特定され、測定可能な形で定義されている
  • 主要な制約(予算、期限、チームスキル、既存システム)が明確になっている

構造

  • モジュール/サービスの境界がドメインに沿っている
  • 依存関係の方向が適切で、循環依存がない
  • 各データの所有者が明確である
  • 採用したスタイルが、規模とチーム構成に見合っている

信頼性・性能

  • 単一障害点が特定され、対策されている
  • 外部呼び出しにタイムアウト・リトライ・サーキットブレーカーが設定されている
  • スケール方針(水平/垂直)とボトルネックの想定がある
  • バックアップと復旧手順が定義されている

セキュリティ

  • 認証・認可の方式が明確である
  • 機密データの暗号化(保存時・通信時)が考慮されている
  • シークレット管理の方法が決まっている
  • 脅威モデリングを実施している

運用

  • ログ・メトリクス・トレースの方針が決まっている
  • SLO とアラート方針が定義されている
  • デプロイとロールバックの手順が自動化されている

記録・進化

  • 重要な判断が ADR として記録されている
  • アーキテクチャ図が最新の状態に保たれている
  • 守るべきルールが自動テスト(適応度関数)で検証されている

参考文献

  • Robert C. Martin『Clean Architecture』
  • Eric Evans『エリック・エヴァンスのドメイン駆動設計』
  • Martin Kleppmann『データ指向アプリケーションデザイン』
  • Mark Richards, Neal Ford『ソフトウェアアーキテクチャの基礎』
  • Neal Ford ほか『進化的アーキテクチャ』
  • Sam Newman『マイクロサービスアーキテクチャ』
  • Google『SRE サイトリライアビリティエンジニアリング』

0件のページ