アプリケーションアーキテクチャ
概要
初心者から実務者向けの包括的解説
アプリケーションアーキテクチャ は、ソフトウェアを「長く・大きく・安全に」育てるための設計技法の体系です。コードが動くかどうかだけでなく、5 年後も保守できるか、10 倍のユーザーに耐えられるか、チームで協働できるか。その土台を決めるのがアーキテクチャです。この章では、現代のアプリケーションアーキテクチャを体系的に整理します。
この章の対象
この章は次の読者を想定しています。
- 初心者向け: アーキテクチャとは何か、なぜ重要かを知りたい方
- 中級エンジニア向け: モノリス・マイクロサービス・DDD・Clean Architecture の具体像を掴みたい方
- テックリード / アーキテクト向け: スケーラビリティ・可用性・選定判断の根拠を深めたい方
- PM・経営層向け: アーキテクチャ決定が事業にどう影響するか理解したい方
この章で重視すること
- アーキテクチャを図や用語ではなく、変更に強い構造として理解する
- モノリスとマイクロサービスを対立ではなく文脈依存の選択として見る
- API、データ、可観測性、デプロイまで含めて全体設計として捉える
ソフトウェア工学と往復しながら、設計と実装の橋を意識する
2026年のアーキテクチャ潮流
2020 年代後半の実装現場で重要な流れ:
- モノリス回帰 — マイクロサービス一辺倒から「適正サイズ」へ回帰(Modular Monolith、Majestic Monolith)
- AI 統合前提 — LLM/RAG/ベクトル DB が標準レイヤに
- Edge と Serverless の成熟 — Cloudflare Workers、Vercel、Deno Deploy など
- プラットフォームエンジニアリング — 内部開発者プラットフォーム(IDP)で組織全体の生産性を上げる
- Observability 第一 — OpenTelemetry、eBPF、Distributed Tracing の標準化
- 契約駆動開発 — OpenAPI / gRPC / AsyncAPI による API ファースト
- Event-Driven とストリーミング — Kafka、EventBridge、NATS、リアルタイム体験
- TypeScript / Rust / Go 優勢 — 型安全・速度・所有権の恩恵
目次
- アーキテクチャとは何か
- なぜアーキテクチャが重要か
- アーキテクチャの基本原則
- アーキテクチャスタイル全体像
- モノリシックアーキテクチャ
- 階層化アーキテクチャ
- クリーンアーキテクチャとヘキサゴナル
- ドメイン駆動設計(DDD)
- MVC / MVP / MVVM
- マイクロサービスアーキテクチャ
- イベント駆動アーキテクチャ
- CQRS と Event Sourcing
- サーバレスアーキテクチャ
- マイクロフロントエンド
- API 設計
- データアーキテクチャ
- キャッシング戦略
- メッセージングパターン
- スケーラビリティパターン
- 可用性パターン
- 分散システムパターン
- 認証認可アーキテクチャ
- オブザーバビリティ
- セキュリティアーキテクチャ
- テスト戦略
- CI/CD とデプロイパターン
- 選び方とトレードオフ
- 2025-2026 最新動向
- まとめ
附録:
- 附録B 代表的デザインパターン集
- 附録C アンチパターン集
- 附録D システム設計面接問題集
- 附録E モノリス→マイクロサービス移行戦略
- 附録F Architecture Decision Record (ADR)
- 附録G よくある質問(FAQ)
- 参考文献
アーキテクチャとは何か
初心者向けメモ: アーキテクチャとは「建築」の比喩で、ソフトウェアの大きな構造を決める設計のことです。「どこに何を置くか」「何と何を分けるか」「どう組み合わせるか」を決める行為。コードを書き始める前の土台づくりです。
定義
Martin Fowler の有名な定義:
Architecture is about the important stuff. Whatever that is. (アーキテクチャとは重要なことを扱う。それが何であれ。)
もう少しフォーマルには、IEEE 42010 の定義:
The fundamental concepts or properties of a system in its environment embodied in its elements, relationships, and in the principles of its design and evolution. (システムの要素・関係・設計原則・進化を規定する、環境における基本概念または特性)
つまりアーキテクチャは:
- 構造:何と何がどう組み合わさるか
- 関係:コンポーネント間の依存・通信
- 原則:なぜこの構造を選んだか
- 進化:将来どう拡張するか
を決めるものです。
コードとアーキテクチャの違い
【図1】抽象化レベル:
| 層 | 関心事 | 変更の困難さ |
|---|---|---|
| ビジネス要求 | 何を達成したいか | - |
| アーキテクチャ | システム全体の構造 | 非常に困難(大規模リライト) |
| 設計 | モジュール・クラス構造 | 困難(リファクタ) |
| コード | 具体的な処理 | 容易(変更・追加) |
アーキテクトの仕事
アーキテクトは単に図を描く人ではなく:
- 意思決定:トレードオフを評価して選択
- 制約の明示:何をやっていい・ダメか
- コミュニケーション:開発者・PM・経営層を翻訳
- 進化:変化に対応して設計を更新
- 教育:チームに設計原則を浸透させる
Fred Brooks の箴言:
「概念的完全性(conceptual integrity)が、システム設計の最も重要な考慮事項である」
なぜアーキテクチャが重要か
初心者向けメモ: 小さな個人プロジェクトではアーキテクチャはあまり重要に見えません。でも人数・コード量・期間が増えるにつれて、その重要性は指数関数的に上がります。「動けばよい」が通用するのは最初だけです。
アーキテクチャ負債の重力
悪いアーキテクチャは 技術的負債 として蓄積します。
【図2】技術的負債の増加カーブ:
データベースの 1 カラム追加に数日、簡単な機能追加に数週間、リファクタに数ヶ月 — このような 「変更が高コスト」な状態 が、悪いアーキテクチャが生む最大の損失です。
アーキテクチャが影響する非機能要件
機能要件(何ができるか)と対比される 非機能要件(NFR) の多くがアーキテクチャで決まります。
| 非機能要件 | 意味 | 例 |
|---|---|---|
| パフォーマンス | 応答速度 | 99%ile で 200ms 以内 |
| スケーラビリティ | 負荷対応力 | 秒間 10万リクエスト |
| 可用性 | 稼働率 | 99.95%(月 22 分停止まで) |
| 信頼性 | 障害耐性 | 単一障害で全停止しない |
| 保守性 | 変更の容易さ | 新機能を 1 週間でリリース |
| テスト容易性 | テストの書きやすさ | 単体・結合・E2E のバランス |
| セキュリティ | 脅威への耐性 | 多層防御、最小権限 |
| 可観測性 | 状態の見えやすさ | ログ・メトリクス・トレース |
| コスト効率 | 経済性 | リソース使用最適化 |
| コンプライアンス | 法規制順守 | GDPR、個人情報保護法 |
Conway の法則
「システムを設計する組織は、その組織のコミュニケーション構造をそのまま反映した構造のシステムを設計する」 — Melvin Conway, 1967
チーム構成が 3 つなら、自然にシステムも 3 つに分かれる傾向があります。逆に 「望ましいシステム構造に合わせてチームを作る」逆コンウェイ戦略 が、モダンな組織設計の核心です。
アーキテクチャの ROI
- 良いアーキテクチャ:初期は遅く見えても、長期的に生産性を維持
- 悪いアーキテクチャ:初期は速く進むが、数年で破綻・書き直し
世界最大級の企業(Netflix、Amazon、Google)は、アーキテクチャに年間数百〜数千人月を投資しています。
アーキテクチャの基本原則
初心者向けメモ: すべてのアーキテクチャスタイルの土台には、数個の基本原則があります。これを押さえれば、新しいパターンに出会っても「なぜそうするのか」が理解しやすくなります。
SOLID 原則
オブジェクト指向設計の 5 原則(Robert C. Martin 提唱)。
| 原則 | 意味 |
|---|---|
| S Single Responsibility Principle | 単一責任:1 クラスは 1 つの理由で変更される |
| O Open/Closed Principle | 開放/閉鎖:拡張に対して開き、修正に対して閉じる |
| L Liskov Substitution Principle | リスコフ置換:派生クラスは基底クラスと置換可能 |
| I Interface Segregation Principle | インターフェース分離:クライアントが使わないメソッドに依存させない |
| D Dependency Inversion Principle | 依存性逆転:抽象に依存し、具象に依存しない |
その他の必須原則
- DRY(Don’t Repeat Yourself):同じ知識を二度書かない
- KISS(Keep It Simple, Stupid):複雑さは必要悪、シンプルに
- YAGNI(You Aren’t Gonna Need It):今必要ないものは作らない
- Separation of Concerns(関心の分離):異なる関心を別モジュールに
- High Cohesion, Low Coupling(高凝集・低結合):モジュール内はまとまり、モジュール間は疎に
- Principle of Least Astonishment(驚き最小):予測どおりに動くように設計
- Composition over Inheritance(継承より合成):is-a より has-a
- Fail Fast(早く失敗):問題は早期に発覚させる
- Idempotency(冪等性):同じ操作を何度実行しても結果が変わらない
アーキテクチャの二大テンション
ほとんどのアーキテクチャ判断は、以下の 2 つの軸上のトレードオフです。
【図3】アーキテクチャの基本トレードオフ:
- シンプル ⇔ 複雑:複雑さはスケーラビリティや柔軟性と引き換え
- 密結合 ⇔ 疎結合:疎結合は独立デプロイ可能だが、ネットワーク越しの遅延
アーキテクチャスタイル全体像
初心者向けメモ: アーキテクチャには多数のスタイルがあります。「どれが一番良い」というものはなく、規模・期限・チーム・事業要件で選びます。以下はよく使われるスタイルの俯瞰図です。
主要スタイルマップ
【図4】アーキテクチャスタイルの関係:
規模 × 複雑度による選択
| 規模 | 推奨スタイル |
|---|---|
| 小(〜数万ユーザー、2-5 人チーム) | モノリス |
| 中(〜数百万ユーザー、10-50 人) | モジュラモノリス、Clean Architecture |
| 大(数千万〜、50 人以上) | マイクロサービス、イベント駆動 |
| エッジ・大量並列 | サーバレス、Edge Functions |
| 複雑なドメイン・規制 | DDD + CQRS + Event Sourcing |
モノリシックアーキテクチャ
初心者向けメモ: モノリス(一枚岩)は「すべてが 1 つのアプリに入った」構造です。「古い・悪い」と誤解されがちですが、多くのスタートアップや中規模プロダクトで最適解です。マイクロサービスより 10 倍速く作れることがあります。
定義
すべての機能を 単一のコードベース・単一のデプロイ単位 で実装するアーキテクチャ。
【図5】モノリシックアーキテクチャ:
種類
1. ビッグボールオブマッド(Big Ball of Mud)
構造のない、スパゲッティ化したモノリス。アンチパターン。避けるべき。
2. モジュラモノリス(Modular Monolith)
1 つのデプロイ単位の中で、明確なモジュール境界 を持つ。最近再評価されている。
3. マジェスティックモノリス(Majestic Monolith)
DHH(Basecamp / Ruby on Rails)が提唱。小規模チームで高速開発できる洗練されたモノリス。
モノリスのメリット
- 開発速度が速い:1 つのリポジトリ、1 つの言語、1 つのデプロイ
- デバッグが簡単:全てが同じプロセス、スタックトレース完全
- トランザクション:単一 DB で ACID 保証
- 運用が単純:1 つのデプロイパイプライン
- コストが低い:サーバ台数・運用コスト削減
モノリスの課題
- スケールの天井:全コードが同じプロセス、水平スケールしかない
- デプロイが重い:小変更でも全体再デプロイ
- 技術選択の硬直:言語・フレームワークが固定
- コードベースが巨大化:数百万行になると複雑
典型例
- Shopify(Ruby on Rails、数十億ドル事業)
- Basecamp
- GitHub(初期〜中期)
- Stack Overflow(.NET、5 台のサーバで数億 PV)
階層化アーキテクチャ
初心者向けメモ: 階層化(Layered)は最も古典的なアーキテクチャです。プレゼンテーション層、ビジネスロジック層、データアクセス層といった 横方向の層 に分ける構造。初心者にも分かりやすく、多くのフレームワーク(Spring、Laravel、Rails、Django)の標準構造です。
典型的な 3 層アーキテクチャ
【図6】3 層アーキテクチャ:
各層の責務
- プレゼンテーション層:ユーザー入力の受付、レスポンス整形(HTTP ハンドラ、UI)
- ビジネスロジック層:業務ルール、ワークフロー(サービスクラス、ドメインモデル)
- データアクセス層:永続化、DB 操作(リポジトリ、ORM)
4 層以上の例
Controller → Application Service → Domain → Repository
メリット・デメリット
メリット:
- 分かりやすい
- 役割分担が明確
- テストしやすい
デメリット:
- 層を貫く変更が多発(1 機能追加で全層を触る)
- ビジネスロジックがサービス層に集中しがち(Anemic Domain Model)
- 横断的関心事(ログ、認証)の扱いが面倒
クリーンアーキテクチャとヘキサゴナル
初心者向けメモ: Clean Architecture(Robert C. Martin)、Hexagonal Architecture(Alistair Cockburn)、Onion Architecture(Jeffrey Palermo) — これらは 「同じ思想の別表現」 です。共通するキーワードは「ドメインを外側に依存させない」。難しく見えて本質はシンプルです。
コア思想:依存の方向を逆転する
【図7】クリーンアーキテクチャの依存方向:
重要なのは矢印の向き:
- UI、Infrastructure → Application → Domain
- Domain は何にも依存しない(純粋な業務ロジック)
- Infrastructure は Application のインターフェースを実装する(依存性逆転)
層の説明
具体例:注文ユースケース
// Domain(純粋、フレームワーク非依存)
class Order {
constructor(
public readonly id: OrderId,
public readonly items: OrderItem[],
public readonly customer: CustomerId
) {}
public total(): Money {
return this.items.reduce((sum, i) => sum.add(i.subtotal()), Money.zero());
}
}
// Application(インターフェース定義)
interface OrderRepository {
save(order: Order): Promise<void>;
findById(id: OrderId): Promise<Order | null>;
}
class PlaceOrderUseCase {
constructor(
private readonly orderRepo: OrderRepository,
private readonly paymentGateway: PaymentGateway
) {}
async execute(cmd: PlaceOrderCommand): Promise<OrderId> {
const order = Order.createFrom(cmd);
await this.paymentGateway.charge(order.total(), cmd.paymentMethod);
await this.orderRepo.save(order);
return order.id;
}
}
// Infrastructure(インターフェースを実装)
class PostgresOrderRepository implements OrderRepository {
async save(order: Order): Promise<void> {
// DB 実装
}
// ...
}
ヘキサゴナル(Ports and Adapters)
本質は同じ。「ポート」(インターフェース)と「アダプター」(実装)で内外を区切る。
【図8】ヘキサゴナルアーキテクチャ:
メリット
- テストが書きやすい:ドメイン層を単独テスト可
- 技術変更に強い:DB を PostgreSQL → MongoDB へ切り替え可
- ドメインが保護される:業務ロジックがフレームワークに汚染されない
- 複雑なドメインに向く:DDD と相性が良い
デメリット
- 初期コスト高:小プロジェクトでは過剰
- コード量が増える:インターフェース、DTO など
- 学習曲線:チーム全員の理解が必要
ドメイン駆動設計(DDD)
初心者向けメモ: DDD(Domain-Driven Design)は Eric Evans が 2003 年に提唱した、複雑なビジネスドメインを扱うための設計アプローチです。用語の揺れを直し、業務エキスパートと開発者が同じ言葉で話すことから始まります。DDD は万能薬ではなく、複雑なドメインの時こそ威力を発揮します。単純な CRUD には過剰です。
8.1 DDD の哲学と歴史
DDD は単なる設計パターン集ではなく、ソフトウェア開発の哲学 です。核心は以下の 3 つ:
- ドメインが最優先:技術ではなく業務の複雑性に焦点
- モデルとコードを一致させる:設計書とコードの乖離を排除
- 業務エキスパートと協働:ドメイン知識を深めながら開発
歴史
| 年 | できごと |
|---|---|
| 2003 | Eric Evans “Domain-Driven Design”(青本)出版 |
| 2013 | Vaughn Vernon “Implementing Domain-Driven Design”(赤本)— 実装寄り |
| 2015 | Alberto Brandolini “Event Storming” 提唱 |
| 2016 | Vaughn Vernon “Domain-Driven Design Distilled”(緑本)— 入門 |
| 2020 以降 | マイクロサービス時代で DDD 再評価、CQRS/ES との融合 |
| 2024-2025 | “Learning Domain-Driven Design”(Vladik Khononov)が新定番 |
8.2 DDD が向く場面・向かない場面
向く:
- 複雑なビジネスルール(金融、保険、ヘルスケア、物流、ERP、EC)
- 長期運用(5-10 年以上のシステム)
- 多数のステークホルダーと業務エキスパート
- チームが大きく、複数のサブドメインに分かれる
- マイクロサービスを適切に分割したい
向かない:
- 単純な CRUD アプリ(ブログ、問い合わせフォーム)
- データ変換中心(ETL パイプライン)
- 実験的プロトタイプ
- 小規模・短期プロジェクト
8.3 ユビキタス言語(Ubiquitous Language)
業務エキスパート、PM、開発者、コード、ドキュメントすべてで 同じ用語 を使う。DDD の出発点かつ最重要。
悪い例:
- コード内:
UserModel、ClientEntity、AccountDTO - PM の資料:「会員」
- 営業:「顧客」
- 契約書:「加入者」
→ 翻訳ミス・認識齟齬が頻発。
良い例:
- 業務もコードもドキュメントも「Member(会員)」で統一
- コードに
class Memberと書く - 会議でも「Member が Subscription を Upgrade した」と話す
ユビキタス言語の育て方
- 業務エキスパートと会話:用語を記録
- 用語集(Glossary)を作る:プロジェクト Wiki に
- 曖昧な用語は警告:「請求」vs「回収」vs「支払」の違いを明確化
- コードに反映:クラス名・メソッド名を自然言語で
- 定期的に更新:ドメイン理解が深まれば進化
8.4 戦略的 DDD(Strategic Design)
DDD の 大局的な部分。どこに投資し、どう分割するかを決める。
8.4.1 ドメインとサブドメイン
一つの事業は複数のサブドメインから成る。例:EC サイト
【図DDD-1】EC のサブドメイン分類:
| 種類 | 投資 | 判断 |
|---|---|---|
| コアドメイン(Core) | 最大投資、最優秀エンジニア | 自社実装、DDD 適用 |
| サポートドメイン(Supporting) | 中程度 | 自社実装または外注 |
| 汎用サブドメイン(Generic) | 最小 | SaaS 購入、OSS 利用 |
経営判断として: 「コアドメインに差別化集中、それ以外は買う」が原則。
8.4.2 境界づけられたコンテキスト(Bounded Context)
同じ言葉でも、文脈で意味が違う という事実を受け入れる設計。
例:「商品(Product)」
- 販売コンテキスト:価格、割引、在庫数、レビュー
- 在庫コンテキスト:SKU、倉庫位置、入荷予定、棚番号
- 配送コンテキスト:サイズ、重量、梱包形態、配送業者
- 請求コンテキスト:単価、税率、通貨、会計コード
「全部まとめた Product クラス」を作ると破綻する。コンテキストごとに別のモデル を持つ。
【図DDD-2】Bounded Context の分離:
Bounded Context の境界は、チーム境界・DB 境界・デプロイ境界 と一致させるのが理想(Conway の法則)。
8.4.3 コンテキストマップ(Context Map)
複数の Bounded Context の 関係性を可視化 する図。DDD における「組織図」にあたる。
Context Map の 9 パターン
Eric Evans が定義した、コンテキスト間の関係を表す 9 パターン:
【図DDD-3】Context Map パターン:
| パターン | 意味 | 典型例 |
|---|---|---|
| Partnership | 2 チームが成功失敗を共有。対等な協力 | 同じ製品の 2 コンテキスト |
| Shared Kernel | 小さな共有モデルを両方で管理 | 共通の顧客 ID モデル |
| Customer-Supplier | 上流(Supplier)が下流(Customer)のニーズを考慮 | ユーザー管理 ← 注文サービス |
| Conformist | 上流の変更に下流が追従するのみ | 外部 SaaS API を使う |
| Anticorruption Layer (ACL) | 外部モデルから自分を守る変換層 | レガシー / 外部 API 連携 |
| Open Host Service (OHS) | 公開プロトコルでサービス提供 | REST / gRPC 公開 API |
| Published Language (PL) | 共通言語で情報交換 | JSON Schema、Protobuf |
| Separate Ways | 連携せず完全独立 | 独立事業部 |
| Big Ball of Mud | 混沌。避けたいが現実にはある | レガシー内部 |
ACL(Anticorruption Layer)の重要性
特に注目すべきは ACL。外部システムやレガシーの「汚い」モデルが自分のコアドメインを汚染しないよう、変換層で守る。
// 外部 API からのデータ
interface ExternalCustomerDTO {
customer_id: string;
first_name: string;
last_name: string;
email_address: string;
status: string; // "A" / "I" / "X" (意味不明)
}
// ACL で自分のドメイン用に変換
class CustomerAntiCorruptionLayer {
toDomain(external: ExternalCustomerDTO): Customer {
return new Customer(
new CustomerId(external.customer_id),
new FullName(external.first_name, external.last_name),
new Email(external.email_address),
this.mapStatus(external.status)
);
}
private mapStatus(s: string): CustomerStatus {
switch (s) {
case "A": return CustomerStatus.Active;
case "I": return CustomerStatus.Inactive;
case "X": return CustomerStatus.Suspended;
default: throw new Error(`Unknown status: ${s}`);
}
}
}
8.5 戦術的 DDD(Tactical Design)
個々のコンテキスト内で使う 実装パターン集。
8.5.1 Value Object(値オブジェクト)
識別子ではなく、属性で等価性を判断。不変(immutable)。
特徴:
- 属性がすべて同じなら「同じもの」
- 作成後は変更不可
- 副作用フリー
- 小さく自己完結
例:金額(Money)
class Money {
constructor(
public readonly amount: number,
public readonly currency: Currency
) {
if (amount < 0) throw new Error("Amount must be non-negative");
Object.freeze(this);
}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error("Cannot add different currencies");
}
return new Money(this.amount + other.amount, this.currency);
}
multiply(factor: number): Money {
return new Money(this.amount * factor, this.currency);
}
equals(other: Money): boolean {
return this.amount === other.amount && this.currency === other.currency;
}
}
const price = new Money(1000, Currency.JPY);
const tax = price.multiply(0.1);
const total = price.add(tax); // 新しい Money が返る、元の price は変わらない
Value Object にすべき候補:
- 金額、通貨、数量
- 住所、郵便番号、電話番号
- メールアドレス、URL
- 日付範囲、期間
- 座標、温度、距離
- ID(
OrderId、CustomerIdなど)
Primitive Obsession(プリミティブ強迫観念)の回避:
// 悪い例:プリミティブ型だらけ
function transferMoney(from: string, to: string, amount: number): void { ... }
// 呼び出し側で順序ミスしても型エラーにならない
transferMoney("123", "456", 1000);
transferMoney(1000, "123", "456"); // ← 違反だがコンパイルエラーにならない
// 良い例:Value Object
function transferMoney(from: AccountId, to: AccountId, amount: Money): void { ... }
transferMoney(new AccountId("123"), new AccountId("456"), new Money(1000, JPY));
// 型で守られる
8.5.2 Entity(エンティティ)
一意の識別子で区別されるオブジェクト。属性が変わっても同じ実体とみなす。
class Order {
constructor(
public readonly id: OrderId, // 識別子
private status: OrderStatus,
private items: OrderItem[],
private customerId: CustomerId
) {}
// ビジネスロジックをクラスメソッドに
addItem(item: OrderItem): void {
if (this.status !== OrderStatus.Draft) {
throw new Error("Cannot modify confirmed order");
}
this.items.push(item);
}
confirm(): void {
if (this.items.length === 0) {
throw new Error("Cannot confirm empty order");
}
this.status = OrderStatus.Confirmed;
}
total(): Money {
return this.items.reduce(
(sum, item) => sum.add(item.subtotal()),
Money.zero()
);
}
// 識別子で等価判定
equals(other: Order): boolean {
return this.id.equals(other.id);
}
}
重要: Entity はデータだけ持つ貧血モデル(Anemic Domain Model)ではなく、ビジネスロジックを持つ のが DDD 流。
8.5.3 Aggregate(集約)
整合性境界 を持つエンティティ群。1 つの Aggregate Root を通じてのみアクセスされ、トランザクションの単位 となる。
例:Order Aggregate
【図DDD-4】Aggregate の境界:
Aggregate の 4 設計ルール(Vaughn Vernon)
- 境界内で真の不変条件をモデル化:Aggregate 内のルールを守る責務
- 小さな Aggregate を設計:大きすぎるとロック競合・性能劣化
- 他の Aggregate は ID で参照:オブジェクト参照で繋がない
- 結果整合性で外部を更新:別 Aggregate の変更はドメインイベント経由
悪い例:大きすぎる Aggregate
良い例:小さい Aggregate + ID 参照
8.5.4 Domain Event(ドメインイベント)
ドメインで起きた重要な事実 を表現する不変オブジェクト。過去形で命名する(OrderPlaced、PaymentFailed)。
abstract class DomainEvent {
readonly occurredAt: Date = new Date();
readonly eventId: string = uuid();
}
class OrderPlaced extends DomainEvent {
constructor(
public readonly orderId: OrderId,
public readonly customerId: CustomerId,
public readonly total: Money
) { super(); }
}
class PaymentFailed extends DomainEvent {
constructor(
public readonly orderId: OrderId,
public readonly reason: string
) { super(); }
}
用途:
- Aggregate 間の疎結合通信:直接呼ばずイベント発行
- 監査ログ:完全な変更履歴
- Event Sourcing:状態=イベント列
- 統合:他のコンテキストへの通知
class Order {
private events: DomainEvent[] = [];
place(): void {
this.status = OrderStatus.Placed;
this.events.push(new OrderPlaced(this.id, this.customerId, this.total()));
}
pullEvents(): DomainEvent[] {
const e = this.events;
this.events = [];
return e;
}
}
// アプリケーションサービス側
async function placeOrder(cmd: PlaceOrderCommand) {
const order = await orderRepo.findById(cmd.orderId);
order.place();
await orderRepo.save(order);
// イベント発行(メッセージブローカへ)
const events = order.pullEvents();
await eventPublisher.publishAll(events);
}
8.5.5 Repository(リポジトリ)
Aggregate の永続化抽象。ドメイン層がインターフェースを定義し、インフラ層が実装。
// ドメイン層(純粋、DB 非依存)
interface OrderRepository {
findById(id: OrderId): Promise<Order | null>;
save(order: Order): Promise<void>;
delete(id: OrderId): Promise<void>;
}
// インフラ層(実装)
class PostgresOrderRepository implements OrderRepository {
async findById(id: OrderId): Promise<Order | null> {
const row = await this.db.query(
'SELECT * FROM orders WHERE id = $1', [id.value]
);
return row ? this.toDomain(row) : null;
}
async save(order: Order): Promise<void> {
// SQL / ORM 実装
}
private toDomain(row: any): Order {
// Row → Order オブジェクト変換
}
}
重要:
- リポジトリは Aggregate 単位(各 Aggregate Root ごとに 1 つ)
- コレクション風のインターフェース(
findById,save,remove) - 複雑なクエリ用に Specification パターンと組み合わせる
8.5.6 Domain Service(ドメインサービス)
Entity にも Value Object にも属さない ビジネスロジック。複数 Aggregate を跨る処理など。
// 例:銀行振込(FromAccount と ToAccount の両方を扱う)
class TransferService {
constructor(private accountRepo: AccountRepository) {}
async transfer(from: AccountId, to: AccountId, amount: Money): Promise<void> {
const fromAccount = await this.accountRepo.findById(from);
const toAccount = await this.accountRepo.findById(to);
if (!fromAccount.canWithdraw(amount)) {
throw new Error("Insufficient balance");
}
fromAccount.withdraw(amount);
toAccount.deposit(amount);
await this.accountRepo.save(fromAccount);
await this.accountRepo.save(toAccount);
}
}
注意: ドメインサービスに何でも入れると Anemic Domain Model(貧血ドメインモデル)化する。可能な限り Entity / Value Object にロジックを入れるのが原則。
8.5.7 Application Service(アプリケーションサービス)
ユースケースのオーケストレーション。ドメインサービスを組み合わせ、トランザクション境界を定める。
class PlaceOrderUseCase {
constructor(
private orderRepo: OrderRepository,
private customerRepo: CustomerRepository,
private paymentGateway: PaymentGateway,
private eventPublisher: EventPublisher
) {}
async execute(cmd: PlaceOrderCommand): Promise<OrderId> {
// 1. ドメインオブジェクト取得
const customer = await this.customerRepo.findById(cmd.customerId);
if (!customer) throw new NotFoundError("Customer not found");
// 2. Aggregate 作成
const order = Order.createFrom(cmd, customer);
// 3. 外部サービス呼び出し
await this.paymentGateway.charge(order.total(), cmd.paymentMethod);
// 4. 永続化(ここがトランザクション境界)
await this.orderRepo.save(order);
// 5. イベント発行
await this.eventPublisher.publishAll(order.pullEvents());
return order.id;
}
}
8.5.8 Factory(ファクトリ)
複雑な Aggregate の生成を分離。生成ロジックが複雑な場合のみ。
class OrderFactory {
static createFromCart(cart: ShoppingCart, customer: Customer): Order {
const items = cart.items.map(ci => new OrderItem(
ci.productId,
ci.quantity,
ci.unitPrice
));
return new Order(
OrderId.generate(),
OrderStatus.Draft,
items,
customer.id,
customer.defaultShippingAddress
);
}
}
8.5.9 Specification(仕様)
複雑な条件をオブジェクト化 するパターン。クエリ条件・バリデーション・ビジネスルールに使える。
interface Specification<T> {
isSatisfiedBy(candidate: T): boolean;
and(other: Specification<T>): Specification<T>;
or(other: Specification<T>): Specification<T>;
not(): Specification<T>;
}
class PremiumCustomerSpec implements Specification<Customer> {
isSatisfiedBy(customer: Customer): boolean {
return customer.totalPurchases.greaterThan(new Money(100000, JPY)) &&
customer.registeredYearsAgo() >= 2;
}
// ...
}
// 使用
const spec = new PremiumCustomerSpec()
.and(new JapaneseCustomerSpec())
.and(new ActiveCustomerSpec().not());
const targetCustomers = allCustomers.filter(c => spec.isSatisfiedBy(c));
8.6 DDD のレイヤアーキテクチャ
DDD では典型的な 4 層アーキテクチャを使う。
【図DDD-5】DDD のレイヤアーキテクチャ:
依存のルール(Clean Architecture と同じ):
- Domain 層は何にも依存しない(純粋な業務ロジック)
- Infra は Domain のインターフェースを実装(依存性逆転)
- App は Domain と Infra(インターフェース)を使う
- UI は App を呼ぶ
8.7 Event Storming
Alberto Brandolini が 2013 年頃に提唱した、付箋紙でドメインを可視化するワークショップ手法。DDD の最強の入門ツール。
使う付箋の色と意味
| 色 | 意味 |
|---|---|
| オレンジ | ドメインイベント(OrderPlaced、PaymentFailed)過去形 |
| 青 | コマンド(PlaceOrder、CancelSubscription)命令形 |
| 黄色 | アクター(顧客、管理者、システム) |
| 紫 | ポリシー / ルール(「支払失敗なら 3 日後に再試行」) |
| ピンク | 外部システム(決済 API、メール送信) |
| 緑 | 読み取りモデル(注文一覧、月次レポート) |
| 赤 | ホットスポット(疑問、リスク、議論が必要な箇所) |
ワークショップの流れ
- カオス的に発散:業務エキスパートが思いつくイベントを壁に貼る
- 時間順に並べる:左から右へ時系列
- 因果関係を見つける:何がトリガーか
- コマンドとアクターを紐付ける
- Aggregate 境界を発見:関連イベントをグループ化
- Bounded Context を抽出:自然な切れ目を見つける
実例:EC の Event Storming
[顧客] → [カートに追加] → CartItemAdded
[顧客] → [チェックアウト] → CheckoutStarted
[システム] → [在庫確認] → InventoryChecked
→ 在庫OK? → Yes → [決済依頼] → PaymentRequested
→ No → OutOfStockNotified
[決済API] → PaymentApproved / PaymentDeclined
PaymentApproved → [注文確定] → OrderPlaced
OrderPlaced → [倉庫に通知] → ShippingRequested
[倉庫] → ShippingPrepared → ItemShipped → ItemDelivered
この流れから、以下の Bounded Context が見える:
- カート:CartItemAdded、CheckoutStarted
- 決済:PaymentRequested、PaymentApproved
- 注文:OrderPlaced
- 配送:ShippingRequested、ItemShipped、ItemDelivered
- 在庫:InventoryChecked、OutOfStockNotified
8.8 DDD と Clean Architecture の関係
Clean Architecture(Robert C. Martin)と DDD は 互換・補完関係。
【図DDD-6】DDD × Clean Architecture:
Clean Architecture は 「どう層を分けるか」、DDD は 「ドメイン層で何を書くか」 を示す。セットで使うのが現代的。
8.9 DDD と CQRS / Event Sourcing
DDD の戦術パターンは CQRS・Event Sourcing と強い親和性 を持つ。
組み合わせのメリット:
- Aggregate = コマンドサイドの整合性境界
- Domain Event = Event Sourcing のイベントそのもの
- Read Model = コンテキスト別のビュー
- Bounded Context = マイクロサービスの単位
【図DDD-7】DDD + CQRS + ES の関係:
ただし 3 つを全部採用すると複雑度が爆発するので、コアドメインのみ で検討するのが賢明。
8.10 DDD 導入ステップ
ステップ 1:ユビキタス言語から始める
DDD 全部を一気に入れる必要はない。まず業務エキスパートと会話して、用語集 を作る。それだけでも効果大。
ステップ 2:Event Storming で可視化
壁に付箋を貼ってドメインを可視化。1-2 日のワークショップで大きく理解が進む。
ステップ 3:Bounded Context を見極める
既存システムのどこが混ざっているかを発見。Context Map を描く。
ステップ 4:コアドメインに戦術パターン適用
すべてに DDD を適用しない。コアドメインのみ に集中。
ステップ 5:Anti-Corruption Layer で外界から守る
レガシー・外部 API から自分のドメインを守る ACL を設置。
ステップ 6:必要なら CQRS / ES へ進化
十分複雑なら CQRS や Event Sourcing を検討。
8.11 DDD アンチパターン
アンチ 1:Anemic Domain Model(貧血ドメインモデル)
データだけ持ちロジックがない Entity。サービスクラスにロジックが集中し、結果的に手続き型。
悪い:
class Order {
constructor(public id: string, public items: any[], public status: string) {}
}
class OrderService {
placeOrder(order: Order) {
if (order.items.length === 0) throw new Error("Empty");
order.status = "placed";
}
}
良い:
class Order {
place() {
if (this.items.length === 0) throw new Error("Empty");
this.status = OrderStatus.Placed;
}
}
アンチ 2:DDD Lite(戦術パターンだけ真似る)
Entity、Repository を作るだけで、Ubiquitous Language や戦略的 DDD を無視。形だけ DDD。
アンチ 3:万能 DDD
すべてのコードに DDD を適用。単純な CRUD にまで Entity・Aggregate・Repository を作る過剰設計。
アンチ 4:巨大 Aggregate
1 つの Aggregate に全てを詰め込む。性能劣化、ロック競合、複雑化。
アンチ 5:Context の混在
複数のコンテキストのモデルが混在した巨大クラス(God Customer)。
アンチ 6:ORM ファースト
ORM のテーブル構造からクラスを生成して、「ドメイン駆動」と呼ぶ。実態は Database-Driven。
8.12 言語別実装のヒント
Java(Spring Boot + JPA)
- Spring の DI で Clean Architecture を実現
- JPA @Entity はドメインに侵食する危険あり、Persistence Model と Domain Model を分離 推奨
Axon Frameworkが CQRS/ES 対応
TypeScript / Node.js
class+ 型システムで表現豊かts-pattern、zodでバリデーションNestJSが DDD フレンドリ
Go
- 小さな構造体と インターフェースで表現
- パッケージ単位で Bounded Context を分ける
go-clean-architecture系テンプレート多数
Rust
- 所有権・型システムが DDD と超相性良い
newtypeパターンで Value Object- Rust DDD コミュニティが活発
C# / .NET
- DDD 元祖の Java 系と並んで歴史あり
MediatR(CQRS)、MartenDB(Event Store)、EventFlow等- Microsoft 公式が DDD サンプル提供
8.13 DDD ツール・ライブラリ
| カテゴリ | ツール |
|---|---|
| モデリング | Miro / Mural(Event Storming)、draw.io |
| コンテキストマップ | Context Mapper DSL |
| イベントストア | EventStoreDB、Kafka、Axon Server |
| フレームワーク | Axon(Java)、NEventStore(.NET)、Marten(.NET) |
| プロジェクション | Kafka Streams、Flink、ksqlDB |
8.14 DDD が解決する問題
DDD を正しく適用すると、以下の問題が緩和される:
- 用語の不一致:ユビキタス言語で解決
- ドメイン知識のサイロ化:業務エキスパートとの協働
- Big Ball of Mud:Bounded Context で分離
- Anemic Model による複雑性爆発:戦術パターンで凝集
- マイクロサービス分割の失敗:Context Map で論理的分割
- レガシー統合の汚染:ACL で防御
- 変更の困難さ:モデルとコードの一致で保守性向上
MVC / MVP / MVVM
初心者向けメモ: Web・モバイル開発でよく聞く MVC・MVP・MVVM は、UI を持つアプリケーションの構造パターン です。「View と Model をどう繋ぐか」の 3 つの流派です。
MVC(Model-View-Controller)
User → View → Controller → Model → View
- Model:データ・ビジネスロジック
- View:表示
- Controller:入力を受けて Model 更新、View 選択
採用例: Ruby on Rails、Django、Spring MVC、Laravel、ASP.NET MVC
MVP(Model-View-Presenter)
View と Model が直接やり取りしない。Presenter が仲介。
採用例: 古典的 Windows Forms、Android(古典的)
MVVM(Model-View-ViewModel)
データバインディングで View と ViewModel を自動同期。
採用例: WPF、SwiftUI(類似)、Vue、Angular(一部)、Knockout、React 初期
【図10】MVC/MVP/MVVM の違い:
マイクロサービスアーキテクチャ
初心者向けメモ: マイクロサービスは「1 つの大きなアプリを、小さな独立したサービス群に分割する」アーキテクチャです。Netflix、Amazon、Google のような大規模システムが採用し、「今風」のイメージが強いですが、複雑さ・運用コストは大きく、小〜中規模では過剰になりがちです。
定義
Martin Fowler & James Lewis の定義(2014):
Microservices is an architectural style that structures an application as a collection of small, autonomous services, modeled around a business domain.
特徴
- 独立デプロイ可能:サービスごとに別々にリリース
- 境界は明確:各サービスは独自の DB・API を持つ
- 技術の多様性:Node.js、Java、Go、Rust を混在可
- ビジネスドメインに沿う:技術レイヤではなく業務機能で分割
- 小さなチーム:「2 枚のピザ」ルール(8-10 人)
典型的構成
【図11】マイクロサービスアーキテクチャ:
マイクロサービスのメリット
- 独立スケール:負荷の高いサービスだけスケールアウト
- 技術の自由度:言語・フレームワーク・DB を用途別に最適化
- 障害隔離:1 サービス障害が全体停止に直結しない
- 組織スケール:数百人チームが並行開発可能
マイクロサービスの課題
- 運用複雑度:数十〜数百のサービスの管理
- 分散トランザクション:ACID が効かない、Saga パターン必須
- ネットワーク遅延:関数呼び出しが HTTP/gRPC 呼び出しに
- デバッグ困難:分散トレーシング必須
- データ整合性:Eventually Consistent が基本
- コスト増:インフラ、人材、ツール
- テスト困難:結合テストが複雑
マイクロサービスの必須ツールチェーン
| 領域 | ツール |
|---|---|
| コンテナ | Docker、containerd |
| オーケストレータ | Kubernetes |
| サービスメッシュ | Istio、Linkerd、Consul Connect |
| API Gateway | Kong、Envoy、AWS API Gateway |
| メッセージング | Kafka、RabbitMQ、NATS、SQS |
| 分散トレーシング | Jaeger、Zipkin、OpenTelemetry |
| メトリクス | Prometheus、Grafana |
| ログ集約 | Elasticsearch + Kibana、Loki |
| サービスディスカバリ | Consul、etcd、Kubernetes DNS |
| 設定管理 | Spring Cloud Config、AWS Parameter Store |
| Secrets | HashiCorp Vault、AWS Secrets Manager |
| CI/CD | Argo CD、Flux、GitHub Actions |
分散システムの誤謬(8 Fallacies)
マイクロサービスを設計する前に知るべき、Peter Deutsch による警告(1994-97):
- ネットワークは信頼できる
- レイテンシはゼロ
- 帯域は無限
- ネットワークはセキュア
- トポロジーは変わらない
- 管理者は 1 人
- 転送コストはゼロ
- ネットワークは均質
全部 間違い。これを知らずに分散化すると地獄を見る。
イベント駆動アーキテクチャ
初心者向けメモ: イベント駆動アーキテクチャ(EDA)は、「サービス同士が直接呼び合う」のではなく、「イベントを発行し、それに反応する」方式です。疎結合・非同期・スケーラブルが特徴で、リアルタイム性・拡張性が求められる現代システムの必須パターンです。
基本構造
【図12】イベント駆動アーキテクチャ:
2 つの主要パターン
1. Event Notification(イベント通知)
「何かが起きた」を通知するだけ。受信側が必要なら別途情報を取りに行く。
OrderPlaced { orderId: "123" }
2. Event-Carried State Transfer(状態転送)
イベント内に完全な情報を含める。受信側は追加クエリ不要。
OrderPlaced {
orderId: "123",
customer: { ... },
items: [ ... ],
total: 5000,
timestamp: "..."
}
メッセージブローカ
| ブローカ | 特徴 | 用途 |
|---|---|---|
| Apache Kafka | 高スループット、永続化 | ログ・分析・ストリーミング |
| RabbitMQ | AMQP、機能豊富 | タスクキュー、RPC |
| NATS | 軽量、高速 | マイクロサービス内部 |
| AWS SQS | マネージド、FIFO | AWS 内サービス連携 |
| AWS EventBridge | スキーマレジストリ | AWS イベントバス |
| Google Pub/Sub | グローバル、マネージド | GCP |
| Azure Event Grid | マネージド | Azure |
| Redis Streams | 軽量、Redis 統合 | 小規模 |
Push vs Pull
- Push(Webhooks 等):発行者が受信者を呼ぶ。シンプルだが失敗時再送困難
- Pull(Kafka):受信者がメッセージを取りに来る。スケール・耐障害性に優れる
イベント駆動のメリット
- 疎結合:発行者は誰が聞いているか知らない
- 非同期:遅いサービスが速いサービスを止めない
- 拡張性:新しい購読者を追加しても既存に影響なし
- 耐障害性:ブローカがメッセージをバッファ
イベント駆動の課題
- デバッグ困難:非同期フロー追跡が難しい
- 結果整合性:「即座に反映」は期待できない
- 順序保証:分散環境では難しい
- 重複配信:At-least-once が一般的、冪等性必須
- スキーマ進化:イベント形式変更は慎重に
CQRS と Event Sourcing
初心者向けメモ: CQRS は「書き込みと読み込みを別モデルに分ける」、Event Sourcing は「状態でなくイベントの連なりを保存する」パターンです。高度で強力ですが、複雑さが大きいので適用範囲を見極めることが重要。
CQRS(Command Query Responsibility Segregation)
コマンド(書き込み) と クエリ(読み込み) を 別モデル で扱う。
【図13】CQRS:
メリット:
- 読み取り最適化(非正規化ビュー)
- 書き込み整合性(正規化モデル)
- 読み書きが別個にスケール
- 複雑な検索に特化したリードモデル
Event Sourcing
現在の状態ではなく、状態変化(イベント)の履歴 を保存する。
従来:口座残高 = 10,000 円(1 レコード)
Event Sourcing:
AccountOpened (初期 0 円)
Deposited (+5,000 円)
Deposited (+10,000 円)
Withdrawn (-5,000 円)
= 現在 10,000 円
メリット
- 完全な監査証跡:全変更履歴が残る(金融・医療で必須)
- 過去の状態を再現:任意時点の状態を計算可能
- 時間方向のデバッグ:「なぜこうなったか」を追える
- 複数のビューを作成可能:異なるリードモデルを同じイベントから生成
- ビジネス分析:イベント自体が分析対象
Event Sourcing + CQRS の組み合わせ
【図14】Event Sourcing + CQRS:
向くケース・向かないケース
向くケース:
- 金融、会計、銀行(監査要求)
- 医療記録
- ゲーム(リプレイ機能)
- 複雑なドメイン(DDD と相性良い)
- 時間ベース分析が必要
向かないケース:
- 単純な CRUD アプリ
- イベント数が非常に少ない
- チームが慣れていない
- リードモデルの再構築コストが許容できない
主要実装
- EventStoreDB:Event Sourcing 専用 DB
- Apache Kafka:イベントストリームとして
- Axon Framework(Java)
- MartenDB(.NET、Postgres ベース)
サーバレスアーキテクチャ
初心者向けメモ: サーバレスは「サーバを自分で管理しない」アーキテクチャ。関数(Function)単位でコードをアップロードすれば、クラウドが実行してくれます。使った分だけ課金、使わなければゼロ円。小〜中規模のスパイキーなワークロードに最適です。
サーバレスの 2 モデル
1. FaaS(Function as a Service)
個別の関数をアップロード。イベント発生時に実行。
- AWS Lambda:業界標準
- Google Cloud Functions
- Azure Functions
- Cloudflare Workers:V8 Isolate、低レイテンシエッジ
- Deno Deploy
- Vercel Functions
2. BaaS(Backend as a Service)
マネージド DB、認証、ストレージ等を組み合わせて作る。
- Firebase
- Supabase
- AWS Amplify
典型例:サーバレス画像処理
【図15】サーバレス画像処理:
メリット
- 運用ゼロ:サーバ管理、OS パッチ、スケーリング不要
- 課金明確:使用時間・呼び出し回数のみ
- 自動スケール:ゼロから数万同時実行まで
- 短時間開発:個別機能を素早くリリース
課題
- Cold Start:初回実行時の起動遅延(数百 ms〜数秒)
- 実行時間制限:AWS Lambda は 15 分まで
- 状態を持てない:ステートレス必須
- ベンダロックイン:抽象化レイヤ(Serverless Framework 等)で緩和
- デバッグ難:ローカル再現が難しい
- 複雑な依存関係:パッケージサイズ制限
サーバレスが向くケース
- Web API のバックエンド(トラフィックが変動)
- バッチ処理・cron
- イベント処理(S3 アップロード、DB 変更)
- Webhook 受信
- チャットボット
- スケジュールタスク
- 画像・動画処理
サーバレスが向かないケース
- 常時稼働の長時間処理
- WebSocket を多用する常時接続
- 超低遅延要求(ms 以下)
- 高度なカスタム設定が必要
マイクロフロントエンド
初心者向けメモ: フロントエンドでも、バックエンドのマイクロサービスと同様に「大きな SPA を分割する」手法が 2020 年頃から普及しました。それがマイクロフロントエンドです。複数チームが並行開発できる一方、複雑さは増します。
主な実装方式
- Module Federation(Webpack 5):ランタイムでモジュール読み込み
- Web Components:標準技術で独立コンポーネント
- iframe:最も単純だが UX に問題
- Server-side Composition:SSR で統合(Next.js、Nuxt)
- Single-SPA:ルータ統合型
採用例: Spotify、IKEA、DAZN、Zalando
API 設計
初心者向けメモ: API(Application Programming Interface)は、異なるシステム同士を繋ぐ「契約」です。どう設計するかで、使いやすさ・パフォーマンス・拡張性が大きく変わります。ここでは主要 3 方式を比較します。
REST(Representational State Transfer)
Roy Fielding が 2000 年の博士論文で提唱。HTTP の特性を活かす。
GET /users 一覧取得
GET /users/123 個別取得
POST /users 作成
PUT /users/123 更新
DELETE /users/123 削除
原則:
- リソース指向:URL はリソースを表す
- HTTP メソッドで動作表現
- ステータスコード:200, 201, 400, 401, 404, 500
- ステートレス:各リクエストは独立
- HATEOAS(理想):レスポンスに次のアクションへのリンク
GraphQL
Facebook が 2015 年に公開。クライアントが欲しいデータだけをクエリできる。
query {
user(id: "123") {
name
email
posts(first: 10) {
title
createdAt
}
}
}
メリット:
- Over-fetching / Under-fetching 問題を解決
- 型システム(スキーマ)
- 単一エンドポイント
デメリット:
- キャッシュが難しい(GET でなく POST)
- N+1 問題(解決策:DataLoader)
- 複雑なクエリによる DoS リスク
実装: Apollo、Relay、Hasura、URQL
gRPC
Google が 2015 年に公開。Protocol Buffers で定義、HTTP/2 で高速通信。
syntax = "proto3";
service UserService {
rpc GetUser(GetUserRequest) returns (User);
rpc StreamUsers(StreamRequest) returns (stream User);
}
message User {
string id = 1;
string name = 2;
string email = 3;
}
メリット:
- バイナリ、高速
- 双方向ストリーミング対応
- 言語中立(Go、Java、Python、C++ 等)
- 型安全、コード自動生成
デメリット:
- ブラウザ直接対応弱(gRPC-Web が必要)
- デバッグしにくい
- テキスト形式でない
比較表
| 項目 | REST | GraphQL | gRPC |
|---|---|---|---|
| プロトコル | HTTP/1.1, HTTP/2 | HTTP(通常 POST) | HTTP/2 |
| データ形式 | JSON(通常) | JSON | Protobuf(バイナリ) |
| スキーマ | OpenAPI(任意) | 必須 | 必須 |
| クライアント | 任意 | クエリ言語 | 自動生成 |
| ブラウザ | ◎ | ◎ | △(gRPC-Web) |
| マイクロサービス間 | ◯ | ◯ | ◎ |
| リアルタイム | ✕(SSE/WS 別) | Subscription | ストリーミング |
| 学習コスト | 低 | 中 | 中-高 |
| 典型用途 | 公開 API | BFF、モバイル | 内部通信 |
API ゲートウェイと BFF
API Gateway:クライアントとサービス群の間の 唯一の入口。認証、ルーティング、レート制限、ログを集約。
BFF(Backend for Frontend):Web / Mobile / スマートTV など、クライアント種別ごとに異なる API を提供する中間層。
【図16】API Gateway と BFF:
API バージョニング
- URL:
/v1/users、/v2/users - ヘッダ:
Accept: application/vnd.myapi.v2+json - クエリパラメータ:
ベストは URL(明示的、ログに残る)。
データアーキテクチャ
初心者向けメモ: データは現代システムの心臓です。「どこに、どう置くか」の選択が、性能・スケーラビリティ・コストのすべてに直結します。ここでは典型的な選択肢とパターンを整理します。
データベース種類
| 種類 | 代表 | 用途 |
|---|---|---|
| リレーショナル(RDB) | PostgreSQL、MySQL、SQL Server、Oracle | トランザクション、関係性 |
| ドキュメント | MongoDB、Couchbase、Firestore | 柔軟なスキーマ、JSON |
| キーバリュー | Redis、DynamoDB、Memcached | キャッシュ、セッション |
| 列指向 | Cassandra、HBase、ScyllaDB | ビッグデータ書き込み |
| グラフ | Neo4j、Neptune、ArangoDB | SNS、レコメンド |
| 時系列 | InfluxDB、TimescaleDB、Prometheus | メトリクス、IoT |
| 全文検索 | Elasticsearch、Meilisearch | 検索エンジン |
| Vector DB | Pinecone、Weaviate、pgvector | 埋め込み、RAG |
| NewSQL | Spanner、CockroachDB、TiDB、YugabyteDB | 分散 RDB |
| OLAP | Snowflake、BigQuery、Redshift | データウェアハウス |
データアーキテクチャのパターン
1. Database per Service(マイクロサービス)
各サービスが独自の DB を持つ。他サービスのテーブルに直接アクセスしない。
2. Shared Database(モノリス・共有 DB)
1 つの DB に全データ。シンプルだがスケールの天井。
3. CQRS + Event Sourcing
(前述)書き込み用と読み込み用を分離。
4. Data Lake / Data Warehouse / Lakehouse
【図17】現代のデータアーキテクチャ:
Lakehouse(Delta Lake、Iceberg、Hudi)は Lake と WH の良いとこ取り。
シャーディング
データを複数 DB ノードに分散。
- Range Sharding:ID 範囲で分割
- Hash Sharding:ハッシュで分割(均等だが範囲クエリ不可)
- Geographic Sharding:地域で分割
レプリケーション
- Primary-Replica(旧 Master-Slave):書き込みは 1 台、読み取りは複数
- Multi-Primary:複数ノードで書き込み可(競合解決が難しい)
CDC(Change Data Capture)
DB の変更を検知してイベントストリームに流す。Debezium、Maxwell が有名。
キャッシング戦略
初心者向けメモ: 「遅いものを速くする」最もコスパの良い方法がキャッシュです。ただし「整合性」との戦いになります。正しいキャッシュ戦略を選ばないと、古いデータが見える等の問題が起きます。
キャッシュの階層
- CPU キャッシュ(L1/L2/L3)
- RAM / ブラウザキャッシュ
- CDN(CloudFront、Cloudflare、Fastly)
- リバースプロキシ(nginx、Varnish)
- アプリケーションキャッシュ(Redis、Memcached)
- DB キャッシュ(クエリキャッシュ、バッファプール)
主要パターン
1. Cache-Aside(Lazy Loading)
読み込み:
1. キャッシュ確認
2. ヒットなら返す
3. ミスなら DB から取得 → キャッシュに保存 → 返す
書き込み:
1. DB 更新
2. キャッシュ無効化 or 更新
最も一般的。シンプル。
2. Read-Through
アプリはキャッシュのみを知る。キャッシュが DB から自動取得。
3. Write-Through
書き込みはキャッシュ → 同期的に DB。データ一貫性高いが遅い。
4. Write-Behind / Write-Back
書き込みはキャッシュ → 非同期に DB。速いが障害時ロストリスク。
5. Refresh-Ahead
TTL 切れ前に自動リフレッシュ。
無効化戦略
- TTL(Time-To-Live):一定時間で失効
- Event-Based:DB 変更時にパブリッシュ
- Manual:アプリが明示的に削除
「Cache Invalidation Is Hard」
Phil Karlton の有名な格言:
There are only two hard things in Computer Science: cache invalidation and naming things. コンピュータサイエンスで本当に難しいのは 2 つだけ:キャッシュ無効化と名前付け。
典型的な問題
- Thundering Herd(雷鳴問題):TTL 切れで大量リクエストが DB を襲う
- Cache Stampede:同上、解決策は lock、probabilistic early expiration
- Cache Penetration:存在しないキーで DB に毎回問合せ(対策:Negative cache、Bloom filter)
- Cache Avalanche:複数キーが同時失効 → DB 過負荷
メッセージングパターン
初心者向けメモ: サービス間通信は「同期」と「非同期」の 2 種類。メッセージングは非同期の王道です。ここでは典型的な 5 パターンを整理します。
1. Queue(P2P)
1 つのメッセージは 1 つの Consumer のみ受信。タスクキュー向き。
2. Topic / Pub-Sub
1 つのメッセージを複数の Subscriber が受信。イベント通知向き。
3. Request-Reply
非同期だが、相手の応答を待つパターン。通信コストで難しい。
4. Competing Consumers
同じキューを複数 Consumer が読んで負荷分散。
5. Saga パターン
マイクロサービスで分散トランザクションを表現するパターン。補償トランザクション で失敗時の取り消しを定義。
【図18】Saga パターン(Orchestration 型):
配信保証
- At-most-once:0 or 1 回(喪失の可能性)
- At-least-once:1 回以上(重複の可能性、冪等性が必要)
- Exactly-once:ちょうど 1 回(最も難しい、Kafka Transactions など)
スケーラビリティパターン
初心者向けメモ: スケーラビリティには 垂直(Vertical) と 水平(Horizontal) の 2 軸があります。垂直は「サーバを強くする」、水平は「サーバを増やす」。クラウド時代は水平が主流です。
Vertical vs Horizontal Scaling
【図19】スケーラビリティの 2 方向:
ロードバランサ
アルゴリズム:
- Round Robin:順番
- Least Connections:接続数最少へ
- IP Hash:IP ベース、同じユーザーは同じサーバへ
- Weighted:サーバ性能で重み付け
- Consistent Hash:サーバ増減時の再配置最小
ステートレス設計
水平スケールの前提。セッションを DB・Redis に外出し、サーバはステートレスに。
非同期化
「同期的にやる必要がない処理」はキューに入れる。
読み取り分離
Primary-Replica で読み取り専用スケール。
シャーディング
データレベルの水平分割。
CDN
静的コンテンツを世界中のエッジに配信。
Auto Scaling
指標(CPU、メモリ、リクエスト数、カスタムメトリクス)に応じた自動増減。
可用性パターン
初心者向けメモ: 「99.9% の可用性」と聞くと高そうですが、年間 8.76 時間の停止を許容します。99.99% なら 52 分、99.999%(ファイブナイン)なら 5 分。数字の違いは大きく、投資コストも桁違いです。
可用性の計算
99.9% (スリーナイン) 年 8.76 時間の停止
99.95% 年 4.38 時間
99.99% (フォーナイン) 年 52.6 分
99.999% (ファイブナイン) 年 5.26 分
SLA / SLO / SLI
- SLI(Service Level Indicator):実測値
- SLO(Service Level Objective):社内目標
- SLA(Service Level Agreement):顧客契約
Redundancy(冗長化)
【図20】可用性の冗長化階層:
Active-Active vs Active-Passive
- Active-Active:全ノードが処理。負荷分散と冗長を兼ねる
- Active-Passive:1 つが本番、もう 1 つは待機(Hot / Warm / Cold Standby)
Disaster Recovery(DR)
- RPO(Recovery Point Objective):何時間前まで復旧できるか(データ損失許容)
- RTO(Recovery Time Objective):どれだけ早く復旧できるか
| レベル | RPO | RTO | コスト |
|---|---|---|---|
| Backup & Restore | 24h | 数時間 | 低 |
| Pilot Light | 1h | 数十分 | 中 |
| Warm Standby | 数分 | 数分 | 高 |
| Multi-Region Active | 秒 | 即時 | 最高 |
サーキットブレーカ(Circuit Breaker)
障害時に呼び出しを 即座に失敗させる パターン。下流の死亡を感知して、一定時間スキップ。Netflix の Hystrix、Resilience4j が有名。
タイムアウト・リトライ・バックオフ
- Timeout:無限待機を防ぐ
- Retry:一時的失敗を吸収
- Exponential Backoff + Jitter:リトライ嵐を防ぐ
Bulkhead(隔壁)
機能ごとに 別々のリソースプール。1 つが落ちても他は影響なし。
Graceful Degradation(段階的劣化)
全部止めるのではなく、重要機能だけ動かす。例:レコメンドが落ちたらデフォルト表示。
分散システムパターン
初心者向けメモ: 分散システムには固有の難しさがあります。CAP 定理、FLP 不可能性、Byzantine Generals など、古典的な理論的制約を知っておくと、設計判断の根拠になります。
CAP 定理
【図21】CAP 定理の三角形:
分散システムでは 同時に 3 つは満たせない。Partition は避けられないので CP か AP を選ぶ。
- CP:銀行、予約、Google Spanner
- AP:SNS タイムライン、Amazon DynamoDB、Cassandra
PACELC 定理
CAP の拡張。「分断時は C or A」に加え、Else(通常時)は Latency or Consistency。
一貫性モデル
| モデル | 意味 |
|---|---|
| Strict Consistency | リアルタイム順序保証(実装不可能) |
| Linearizability | SC + リアルタイム(最強の実装可能) |
| Sequential Consistency | 全スレッドで一貫順序 |
| Causal Consistency | 因果関係のみ保証 |
| Eventual Consistency | 最終的に収束 |
合意アルゴリズム
- Paxos(Lamport 1989):原典
- Raft(Ongaro 2014):Paxos を分かりやすく、etcd / Consul / TiKV 採用
- Zab:Apache ZooKeeper
- PBFT:ビザンチン耐性
- HotStuff / Tendermint:ブロックチェーン向け
レプリケーション戦略
- Single-Leader(Primary-Replica):シンプル、Read scale
- Multi-Leader:Geo 分散、競合解決必要
- Leaderless(Dynamo-style):Cassandra、DynamoDB
分散トランザクション
- 2PC(Two-Phase Commit):参加者がブロックする、現代では忌避
- Saga パターン:補償トランザクションで長時間 TX 実現
- TCC(Try-Confirm-Cancel):3 段階
Idempotency(冪等性)
分散で 再送が前提 なので、同じ操作を何度実行しても結果が同じになるよう設計。Idempotency-Key ヘッダなど。
認証認可アーキテクチャ
初心者向けメモ: 認証(誰か)と認可(何ができるか)は別物です。現代の Web は「自社で実装」より「IdP(Identity Provider)を活用」が主流。セキュリティ要件とユーザー体験のバランスが重要です。
主要プロトコル
OAuth 2.0 / 2.1
認可の業界標準。「サードパーティに、特定の範囲で操作を許可する」。
User → アプリ → 「Google ログイン」 → Google → アプリに Access Token 発行
主なフロー:
- Authorization Code + PKCE:Web / モバイル(推奨)
- Client Credentials:サーバ間
- Device Code:TV、IoT
- Resource Owner Password:非推奨
- Implicit:非推奨(2.1 で削除)
OpenID Connect(OIDC)
OAuth 2.0 の上に 認証 を乗せたプロトコル。ID Token(JWT)を発行。
SAML 2.0
エンタープライズ SSO の標準。XML ベース、OIDC に比べて重い。
JWT(JSON Web Token)
Header.Payload.Signature
ヘッダ.ペイロード.署名(Base64)
Stateless なトークン。マイクロサービス間で広く使われる。
注意点:
- 秘密鍵管理:署名鍵漏洩は致命的
- Revocation が難しい:ブラックリスト or 短命化
- ペイロード改ざん不可だが読めるので機密情報を入れない
認可モデル
RBAC(Role-Based Access Control)
ロールに権限を紐付け、ユーザーにロールを割当。
Alice → Admin → [read, write, delete]
Bob → Editor → [read, write]
Carol → Viewer → [read]
ABAC(Attribute-Based Access Control)
属性ベース。より細かい制御可能。
IF user.department == resource.owner.department
AND resource.confidentiality != "secret"
AND time.hour IN [9, 18]
THEN ALLOW read
ReBAC(Relationship-Based)
Google Zanzibar / OpenFGA 流。関係を中心に。
alice is owner of document:123
→ alice can edit document:123
認証認可サービス
- Auth0 / Okta
- Microsoft Entra ID
- AWS Cognito
- Firebase Auth
- Keycloak(OSS)
- Supabase Auth(OSS)
- Clerk(モダン SaaS)
オブザーバビリティ
初心者向けメモ: オブザーバビリティ(観測性)は「システムの内部状態を外部から推測できる度合い」。ログ・メトリクス・トレースの 3 本柱で構成されます。マイクロサービス時代は特に重要です。
3 本柱
【図22】Observability の 3 本柱:
Logs
イベントの時系列記録。構造化ログ(JSON)が現代の主流。
{
"timestamp": "2026-04-24T10:30:45Z",
"level": "error",
"service": "order-service",
"trace_id": "abc123",
"user_id": "u456",
"message": "Payment failed",
"error": "insufficient funds"
}
ツール: Elasticsearch + Kibana(ELK)、Loki、Datadog Logs、Splunk、CloudWatch Logs
Metrics
時系列数値データ。集計・アラートに最適。
4 種類の指標:
- Counter:増加のみ(リクエスト数)
- Gauge:現在値(メモリ使用量)
- Histogram:分布(レスポンス時間)
- Summary:パーセンタイル
ツール: Prometheus、Grafana、Datadog、CloudWatch Metrics、New Relic
Traces(分散トレーシング)
1 リクエストが複数サービスを通る際の 全経路 を追跡。
ツール: Jaeger、Zipkin、OpenTelemetry、Datadog APM、New Relic
OpenTelemetry(OTel)
CNCF Graduated。ベンダ中立の観測データ収集標準。2025-2026 年の業界標準。
4 つのゴールデンシグナル(Google SRE)
すべてのサービスで監視すべき 4 指標:
- Latency(遅延)
- Traffic(トラフィック量)
- Errors(エラー率)
- Saturation(飽和度)
RED メソッド
- Rate:秒間リクエスト数
- Errors:エラー数
- Duration:処理時間
USE メソッド
リソースごとに:
- Utilization:使用率
- Saturation:待ち行列
- Errors:エラー数
セキュリティアーキテクチャ
初心者向けメモ: セキュリティは「後付けできない」性質があります。設計段階から組み込む必要があり、多層防御(Defense in Depth) が原則です。
多層防御の例
【図23】多層防御:
原則
- 最小権限(Least Privilege)
- Defense in Depth(多層防御)
- Fail Secure(失敗時は安全側に)
- Zero Trust(暗黙の信頼をしない)
- セキュアデフォルト
脅威モデリング
- STRIDE:Spoofing、Tampering、Repudiation、Information disclosure、Denial of service、Elevation of privilege
- DREAD:Damage、Reproducibility、Exploitability、Affected users、Discoverability
OWASP Top 10(2025)
- Broken Access Control
- Cryptographic Failures
- Injection
- Insecure Design
- Security Misconfiguration
- Vulnerable and Outdated Components
- Identification and Authentication Failures
- Software and Data Integrity Failures
- Security Logging and Monitoring Failures
- Server-Side Request Forgery(SSRF)
暗号化
- 保存時(at rest):dm-crypt、BitLocker、FileVault、TDE
- 転送時(in transit):TLS 1.3
- 使用時(in use):Confidential Computing(TDX、SEV-SNP)
Secret 管理
ハードコード絶対禁止。使うもの:
- HashiCorp Vault
- AWS Secrets Manager
- Google Secret Manager
- Azure Key Vault
- 1Password for Developers
テスト戦略
初心者向けメモ: テストはアーキテクチャの重要な一部です。「テストピラミッド」の原則で、底辺(単体テスト)を厚く、頂点(E2E)を薄く保つのが効率的です。
テストピラミッド
【図24】テストピラミッド:
テストの種類
| 種類 | 範囲 | 速度 | 本数目安 |
|---|---|---|---|
| Unit | 関数 / クラス単位 | ms | 数千〜 |
| Integration | 複数モジュール | 数秒 | 数百〜 |
| Contract | API 契約(Pact) | 数秒 | 数十 |
| E2E | 全体フロー | 分 | 数〜数十 |
| Smoke | 基本動作確認 | 秒 | 数 |
| Load | 負荷 | 分〜時間 | シナリオ数 |
| Chaos | 障害注入 | 実験的 | シナリオ数 |
TDD(Test-Driven Development)
Red → Green → Refactor
失敗するテスト書く → 最小限で通す → リファクタ
BDD(Behavior-Driven Development)
Given When Then で仕様を書く(Cucumber、RSpec)
Given ユーザーがログイン済み
When /dashboard にアクセス
Then ダッシュボードが表示される
Mutation Testing
コードを故意に変異させて、テストが検知できるか確認。Stryker、PITest。
Property-Based Testing
「すべての入力で性質が成り立つ」をランダム入力でチェック。QuickCheck、Hypothesis、fast-check。
CI/CD とデプロイパターン
初心者向けメモ: Continuous Integration / Continuous Delivery は、現代開発の必須プラクティスです。「ビルドは常に緑」「デプロイはボタン一発」「小さく頻繁に」が原則。
CI/CD パイプライン例
【図25】CI/CD パイプライン:
デプロイ戦略
1. Rolling Update
古いインスタンスを少しずつ新しいものに置換。Kubernetes デフォルト。
2. Blue-Green
本番環境を 2 セット持ち、切り替え。即時ロールバック可能。
3. Canary
一部トラフィックを新バージョンへ(例:5%)。メトリクス監視しつつ段階拡大。
4. Feature Flag
コードは全部デプロイ、機能は ON/OFF で制御。LaunchDarkly、Unleash、Split.io。
5. Shadow / Mirror
本番トラフィックを新バージョンにも流すが、レスポンスは捨てる。動作確認。
GitOps
Git を単一の真実源(Source of Truth)として、インフラ・アプリをデプロイ。Argo CD、Flux が代表。
選び方とトレードオフ
初心者向けメモ: 「マイクロサービス or モノリス」「REST or GraphQL」などの選択は、チームサイズ・ドメイン複雑度・組織成熟度 で決まります。ここでは判断フレームを示します。
規模別推奨
| 規模 | アーキテクチャ | データベース | デプロイ |
|---|---|---|---|
| 〜10人、〜10万ユーザー | モノリス | RDB 単体 | VPS、Render、Railway |
| 10-50人、〜100万ユーザー | モジュラモノリス | RDB + Redis | ECS、Cloud Run |
| 50-200人、〜1000万ユーザー | マイクロサービス(少数) | RDB + NoSQL 混在 | Kubernetes |
| 200人以上、1億ユーザー | マイクロサービス+EDA | 多種併用 + Data Lake | 多リージョン K8s |
アーキテクト判断のチェックリスト
- 何を最適化するか:速度 / コスト / 品質
- 何を捨てるか:全部は無理
- 変化の速度:どれくらいの頻度で要求が変わるか
- チーム能力:何ができるか
- 時間軸:3 年後どうなっているべきか
- 可逆性:やめられる判断か
Jeff Bezos の「二方向ドアと一方向ドア」:
- 可逆(二方向):素早く決める
- 不可逆(一方向):慎重に決める
2025-2026 最新動向
初心者向けメモ: アーキテクチャは生きた分野で、毎年新しいパラダイムが登場します。以下は 2025-2026 年に特に重要なトピックです。
1. AI ネイティブアーキテクチャ
- RAG(Retrieval-Augmented Generation):LLM + ベクトル DB で社内知識応答
- Agent アーキテクチャ:LLM がツールを呼び出して自律行動
- ベクトル DB 統合:pgvector、Pinecone、Weaviate、Milvus
- モデルルーティング:リクエスト種別で LLM を使い分け
2. プラットフォームエンジニアリング
- Internal Developer Platform(IDP):Backstage、Port、Spotify 式
- Golden Path:チームが標準構成で素早く始められる
- Platform Team:インフラ・ツールを「製品」として提供
3. サーバレス第 2 世代
- Durable Functions:ワークフロー指向
- Cloudflare Workers / Durable Objects:エッジ常駐
- Deno Deploy、Bun:JavaScript / TypeScript ネイティブ
- WebAssembly(WASM):サーバレスランタイムの中核
4. モノリス回帰
- Amazon Prime Video がマイクロサービスを「分散モノリス」に戻して 90% コスト削減(2023 年の記事)
- Modular Monolith の再評価
- Majestic Monolith(Basecamp)
5. 型駆動開発・ドキュメント
- OpenAPI、gRPC、GraphQL、AsyncAPI:スキーマファースト
- Zod / Pydantic / ts-rest:実行時型検証
- API Contract Testing:Pact
6. リアルタイム体験
- WebSocket の進化(Socket.io、SignalR)
- Server-Sent Events(SSE) で LLM ストリーミング
- WebRTC:P2P 通信
- CRDT(Conflict-free Replicated Data Types):Yjs、Automerge、Figma、Notion
7. データ基盤の進化
- Lakehouse(Delta Lake、Iceberg、Hudi)
- DuckDB:組み込み分析 DB、爆発的普及
- Polars:Pandas より速い Rust 製
- dbt:データ変換の標準
8. Rust / Go / TypeScript 優勢
- Rust:カーネル、CLI、パフォーマンス重視 Web
- Go:クラウドインフラ、マイクロサービス
- TypeScript:フロント・Node.js・Deno
9. eBPF とシステム可視化
- カーネル非侵入で観測・制御
- Cilium(K8s ネットワーク)、Falco(セキュリティ)、Pixie(観測)
10. 開発者体験(DX)最優先
- フレームワーク:Next.js、Remix、SvelteKit、Astro、Nuxt 3
- 即時プレビュー:Vercel、Netlify、Cloudflare Pages
- AI Copilot 統合:VSCode、Cursor、JetBrains
章の整理
アプリケーションアーキテクチャは、ソフトウェアを「長く使える資産」として作るための設計技法 です。具体的なコードより一段階上の抽象で、チーム・事業・時間との対話を含みます。
本書の要点
- アーキテクチャは選択の連続:どれか 1 つが常に正解ということはない
- 基本原則を押さえる:SOLID、DRY、KISS、YAGNI、関心の分離
- スタイルの適材適所:モノリスも立派な選択肢
- 非機能要件を意識:性能、可用性、保守性、セキュリティ
- 分散は難しい:CAP、Conway、Fallacies を知る
- 観測性第一:作る前から監視設計を含める
- 進化を続ける:アーキテクチャは一度で決まらない
普遍の真理
- Simple is better than complex(Pythonic の精神)
- Premature optimization is the root of all evil(Knuth)
- Make it work, make it right, make it fast(Kent Beck)
- The best code is no code at all
2026 年の世界では、AI がコードを書く時代になっても、どういう構造にするか を決める判断力は人間にしかできません。本書があなたのアーキテクチャ旅の良き伴走者となることを願っています。
附録B:代表的デザインパターン集
初心者向けメモ: GoF(Gang of Four)のデザインパターンは古典ですが、現代でもよく使われます。ここでは実務で頻出のパターンを整理します。
B.1 GoF の 23 パターン(抜粋)
生成(Creational)
- Singleton:唯一のインスタンス
- Factory Method:生成処理の抽象化
- Abstract Factory:関連オブジェクト群の生成
- Builder:複雑なオブジェクトを段階的に構築
- Prototype:オブジェクトをコピーして生成
構造(Structural)
- Adapter:互換のないインターフェースを繋ぐ
- Decorator:動的に機能追加
- Facade:複雑なサブシステムに単純な窓口
- Proxy:代理オブジェクト(遅延ロード、アクセス制御)
- Composite:木構造を統一的に扱う
- Bridge:実装と抽象を分離
振る舞い(Behavioral)
- Observer:変化を通知
- Strategy:アルゴリズムを切替可能に
- Command:操作をオブジェクト化
- State:状態によって振る舞い変化
- Iterator:コレクション走査の抽象化
- Template Method:骨格を定義、詳細はサブクラス
- Chain of Responsibility:責務の連鎖
B.2 エンタープライズパターン(Martin Fowler)
- Repository:データアクセス抽象化
- Unit of Work:変更追跡とトランザクション
- Domain Model:豊富なドメインロジック
- Transaction Script:手続き的業務ロジック
- Data Mapper:ORM の基礎
- Active Record:データ行 = オブジェクト(Rails)
- Service Layer:アプリケーションサービス
- Identity Map:同一エンティティの重複読み込み防止
- Lazy Load:遅延読み込み
- Data Transfer Object(DTO):データ運搬専用
B.3 クラウドパターン(Microsoft Cloud Design Patterns)
- Retry:一時的失敗の再試行
- Circuit Breaker:障害時即座失敗
- Bulkhead:リソース隔離
- Throttling:レート制限
- Queue-Based Load Leveling:キューでピーク平準化
- Saga:分散トランザクション
- Competing Consumers:複数 Consumer で負荷分散
- Priority Queue:優先度付きキュー
- Compensating Transaction:失敗時の補償
- Health Endpoint Monitoring:健康状態公開
- Sidecar:補助機能を別コンテナに
- Ambassador:プロキシ的補助コンテナ
- Strangler Fig:レガシーを段階置換
- Anti-Corruption Layer:外部汚染防止
- Gateway Aggregation:複数 API を 1 つに集約
- Gateway Offloading:共通機能(認証等)を Gateway に
- Backend for Frontend:クライアント別 API
- CQRS
- Event Sourcing
- Materialized View:リードモデル事前計算
B.4 分散システムパターン
- Leader Election:リーダー選出
- Sharding:データ分割
- Scatter-Gather:並列実行 + 集約
- Pipeline:段階処理
- Service Registry / Discovery:動的サービス検出
- Circuit Breaker(前述)
- Dead Letter Queue:処理失敗メッセージ保管
B.5 データパターン
- CQRS
- Event Sourcing
- Materialized View
- Change Data Capture(CDC):DB 変更を イベントに
- Transactional Outbox:DB 書き込み + イベント送信の原子化
- Inbox Pattern:冪等なメッセージ処理
- Shared Database(アンチパターン寄り)
- Database per Service(マイクロサービス原則)
附録C:アンチパターン集
初心者向けメモ: 「良いパターン」以上に、「避けるべきパターン」を知ることが重要です。以下は有名なアンチパターン。
C.1 構造のアンチパターン
Big Ball of Mud
構造のない、ごちゃ混ぜのコード。最も多い。
God Object / God Class
1 クラスに数千行、全てを知る巨大オブジェクト。
Lasagna Code
層が多すぎて、何をするにも多層を貫通する。
Spaghetti Code
制御フローがあちこちジャンプ、理解不能。
Copy-Paste Programming
同じコードが 10 箇所にある(DRY 違反)。
Golden Hammer
「ハンマーを持っていると、全てが釘に見える」。1 つの技術を全てに適用。
C.2 マイクロサービスのアンチパターン
Distributed Monolith
マイクロサービスに分けたが、デプロイは常に同時 な状態。最悪の結果(モノリスの弱点 + マイクロサービスの複雑度)。
Nano Services
サービスが細かすぎて、1 機能の変更で 10 サービスを同時変更。
Shared Database
マイクロサービスが同じ DB を共有。結合度高すぎ。
Chatty Services
サービス間の呼び出しが多すぎて、1 リクエストで 100 回通信。
Service Call Chain Hell
A → B → C → D → E の長いチェーン。どれか 1 つ落ちると全滅。
C.3 データのアンチパターン
Anemic Domain Model
ドメインクラスがデータだけ持ち、ロジックがサービスに集中(DDD 的にはアンチ)。
SELECT *
必要ないカラムも取得。性能劣化。
N+1 クエリ
SELECT * FROM orders WHERE user_id = 1; -- 1 回
SELECT * FROM items WHERE order_id = ?; -- N 回
ORM でよく発生。Eager loading で解決。
Missing Indexes
WHERE / JOIN キーに index なし。全スキャン。
C.4 組織・プロセスのアンチパターン
Developer Blame Culture
障害をエンジニア個人の責任にする。心理的安全性を破壊。
Hero Culture
特定の「エース」だけが全てを知る。バス要因(バスファクタ 1)。
Magic Pixie Dust
「マイクロサービスにすれば解決」「クラウドに行けば解決」など、技術で全て解ける幻想。
Architecture Astronaut
抽象度が高すぎて、実際の問題から乖離した設計者。
附録D:システム設計面接問題集
初心者向けメモ: 大手 IT 企業の技術面接では、システム設計問題が定番です。ここでは頻出問題と解き方のフレームを整理します。
D.1 定番問題
- URL 短縮サービス(bit.ly 的)を設計せよ
- Twitter のタイムラインを設計せよ
- Netflix の動画配信を設計せよ
- Uber のマッチングを設計せよ
- Instagram のストーリーを設計せよ
- チャットアプリ(WhatsApp)を設計せよ
- 検索エンジン(Google)を設計せよ
- レート制限システムを設計せよ
- ニュースフィードを設計せよ
- 通知システムを設計せよ
D.2 解き方のフレーム(45 分想定)
-
要件確認(5 分)
- 機能要件:何ができる必要があるか
- 非機能要件:QPS、データ量、SLA
-
規模見積(5 分)
- 月間アクティブユーザー
- QPS(平均・ピーク)
- ストレージ(1 年で何 TB)
- 帯域(MB/s)
-
高レベル設計(10 分)
- クライアント、ロードバランサ、API、DB、キャッシュ、キューのブロック図
-
データモデル(5 分)
- 主要エンティティ、関係、インデックス
-
詳細設計(15 分)
- キー機能のフロー
- ボトルネック分析
- スケーリング戦略
-
トレードオフ議論(5 分)
- 選択肢の比較
- CAP でどちらを優先
- コスト対効果
D.3 URL 短縮サービスの解き方例
要件:
- 長 URL → 短 URL(例:
https://bit.ly/abc123) - リダイレクト
- 月 1 億 URL 作成、10 億リダイレクト
規模見積:
- QPS:1 億 / 30 日 / 86400 秒 ≈ 40 QPS 作成
- リダイレクト QPS:10 億 / 2.5M 秒 ≈ 400 QPS
- 5 年で 60 億 URL、1 URL = 500 bytes → 3TB
設計:
Client → Load Balancer → API Servers → Cache (Redis) → DB (Cassandra / DynamoDB)
↓
Counter Service (Zookeeper)
キー生成:
- Base62 エンコード(0-9, a-z, A-Z)
- 7 文字で 62^7 ≈ 3.5 兆通り
ボトルネック:
- リダイレクトが多い → キャッシュ重視
- キー衝突 → Counter Service で一意性保証
附録E:モノリス→マイクロサービス移行戦略
初心者向けメモ: 「うちもマイクロサービスにしたい」という話はよくありますが、失敗事例の方が多い のが現実です。ここでは慎重な移行戦略を整理します。
E.1 「そもそも移行すべきか」のチェック
以下に Yes が 3 つ以上 ある場合のみ検討推奨:
- [ ] モノリスの規模が 50 万行以上
- [ ] 開発者が 50 人以上、複数チーム
- [ ] デプロイが月 1 回以下になっている
- [ ] 機能ごとに負荷特性が大きく異なる
- [ ] 技術を部分的に刷新したい
- [ ] チーム間のデプロイ衝突が頻発
これらがないなら モジュラモノリスで十分 かもしれません。
E.2 Strangler Fig パターン(Martin Fowler)
イチジクの絞め殺し植物のように、新しいサービスが徐々にモノリスを包んでいく パターン。
【図26】Strangler Fig パターン:
段階:
- Proxy を導入:全リクエストを Proxy 経由にする
- 1 機能を切り出す:新サービス化、Proxy でルート変更
- 繰り返す:機能ごとに移行
- 最後にモノリス廃止
E.3 移行順序の決め方
優先度高:
- 独立性が高い:他機能との結合が少ない
- 変更頻度が高い:切り出すメリット大
- 負荷特性が特殊:独立スケールしたい
- 技術刷新したい:他言語・他 DB にしたい
後回し:
- コアドメイン(複雑すぎる)
- 多数の機能から参照される共通機能
- レガシー DB に依存
E.4 データ移行の戦略
- 共有 DB を維持しつつコードだけ分離(最初のステップ)
- DB テーブル単位でデータオーナーを決める
- 読み取りはレプリケーション or API
- 書き込みは二重書き込み(Dual Write)で移行
- 最終的に別 DB に分離
E.5 よくある失敗
- 先にマイクロサービス化してからドメイン整理 ← 逆
- Distributed Monolith 化(デプロイが同時)
- 観測性なしで移行(障害時の原因不明)
- 組織が従来のまま(Conway の法則で失敗)
- 移行途中で放棄(一番悪い状態)
E.6 成功事例
- Amazon:2000 年代初頭に大規模マイクロサービス化
- Netflix:2008 年の DB 障害を機に移行
- Uber:2013-2017 年に大量マイクロサービス化(ただし逆戻りも多い)
注意: Amazon Prime Video が 2023 年にマイクロサービスを分散モノリスに戻して 90% コスト削減。「常に正解」はない。
附録F:Architecture Decision Record (ADR)
初心者向けメモ: アーキテクチャ判断は忘れられやすい。「なぜこの選択をしたか」を記録する軽量ドキュメント形式が ADR です。docs/adr/ に Markdown ファイルとして Git で管理します。
F.1 ADR テンプレート(Michael Nygard 式)
# ADR-001: リードモデルに Redis を採用
## ステータス
採用(2026-04-24)
## 背景
注文履歴の参照クエリが遅く、DB 負荷が高い。レスポンスタイム P99 で 2 秒超。
キャンペーン時にさらに悪化する。
## 決定
リードモデルを Redis に格納し、書き込み時に更新する。
## 選択肢
### 選択肢 1:Redis キャッシュ
- Pros: 高速、シンプル、運用慣れている
- Cons: メモリコスト、永続化は別途
### 選択肢 2:Elasticsearch
- Pros: 検索機能豊富
- Cons: オーバースペック、学習コスト
### 選択肢 3:リードレプリカ追加
- Pros: 既存構成の延長
- Cons: 根本的遅さは変わらない
## 結果
- 採用:選択肢 1(Redis)
- 期待効果:レスポンスタイム P99 を 200ms 以下に
- 副作用:データ整合性は結果整合
- リスク:Redis ダウン時のフォールバック設計必要
## 関連
- ADR-002: Cache-Aside パターンの採用
- GitHub Issue #123
F.2 ツール
- adr-tools:CLI で管理
- Log4brains:Web UI
- ADR Manager(VS Code extension)
- 単純に Markdown でも十分
F.3 いつ書くか
- 重要な技術選定(言語、フレームワーク、DB)
- アーキテクチャパターン採用(CQRS、マイクロサービス化)
- 可逆でない判断
- チーム内で議論が分かれた判断
附録G:よくある質問(FAQ)
G.1 「モノリスとマイクロサービス、最初どちらで始めるべき?」
A:モノリスから始めよ。 Martin Fowler の “Monolith First” 原則。最初からマイクロサービスで始めると、ドメイン境界が不明確なまま分割して後で痛い目を見る。
G.2 「Clean Architecture は必須?」
A:規模次第。 小規模なら過剰。中〜大規模、長期運用、複雑ドメインなら強く推奨。
G.3 「REST か GraphQL か gRPC か?」
A:
- 公開 API・多様なクライアント:REST
- モバイル・複雑な取得要件:GraphQL
- 内部マイクロサービス・高速通信:gRPC
- 混在も普通(外部 REST、内部 gRPC)
G.4 「SQL か NoSQL か?」
A:デフォルトは SQL。 強い整合性、JOIN、成熟したエコシステム。NoSQL は「明確な理由がある時」に選ぶ:
- 超大規模書き込み → Cassandra
- 柔軟スキーマ → MongoDB
- グラフ関係 → Neo4j
- キャッシュ → Redis
G.5 「Kubernetes は必須?」
A:規模次第。 小規模なら Cloud Run、ECS、Heroku で十分。大規模・複雑なら K8s の恩恵大。
G.6 「アーキテクト は必要?」
A:中規模以上で有益。 ただし「タワーから命令する」のではなく、「開発チームと一緒にコード書くアーキテクト」が理想。
G.7 「新技術はどれくらい採用すべき?」
A:Gartner Hype Cycle を意識する。
- Innovation Trigger:遊び程度
- Peak of Inflated Expectations:避ける(誇張)
- Trough of Disillusionment:学習の好機
- Slope of Enlightenment:本番採用検討
- Plateau of Productivity:安心して導入
G.8 「AI 時代、アーキテクチャの価値は?」
A:むしろ上がる。 コード生成は AI ができるが、「どう構造化するか」「何を分けるか」「どうトレードオフを評価するか」は人間の判断。AI 時代、アーキテクトの価値は増すと予測される。
まとめ
アプリケーションアーキテクチャは、変更に強い構造をどう作るかを考える章です。モノリス、マイクロサービス、API、データ、観測性、運用までをまとめて見ることで、設計判断の軸が整理できます。