ストーリー
なぜDDDがマイクロサービスに必要なのか
マイクロサービスの最大の課題は「どこでサービスを分割するか」です。技術的な境界(データ層、ビジネスロジック層)ではなく、ビジネスドメインの境界で分割することが成功の鍵です。
| 分割基準 | 結果 | 例 |
|---|---|---|
| 技術層で分割 | 分散モノリス | フロント/API/DB各サービス |
| チーム構成で分割 | コンウェイの法則そのまま | チームA用/チームB用サービス |
| ドメインで分割 | 疎結合サービス | 注文/決済/在庫サービス |
戦略的DDD
サブドメインの分類
ビジネスドメインを3種類のサブドメインに分類します:
| 分類 | 定義 | 投資レベル | 例(EC) |
|---|---|---|---|
| Core | 競争優位の源泉 | 最大(内製) | レコメンデーション、価格最適化 |
| Supporting | コアを支える固有機能 | 中(内製/カスタム) | 在庫管理、注文処理 |
| Generic | どのビジネスでも同じ | 最小(SaaS/OSS) | 認証、メール送信、決済 |
ユビキタス言語
同じ用語でもコンテキストによって意味が異なることがあります:
「商品」の意味の違い:
カタログチーム: 商品 = 名前、説明、画像、カテゴリ
在庫チーム: 商品 = SKU、在庫数、倉庫位置
決済チーム: 商品 = 金額、税区分、割引適用
配送チーム: 商品 = 重量、サイズ、配送区分
各チームが自分たちのコンテキスト内で統一された言語を使うことが重要です。
戦術的DDD
Entity(エンティティ)
同一性(ID)を持ち、ライフサイクルを通じて追跡されるオブジェクト:
class Order {
constructor(
readonly id: OrderId, // 同一性
private items: OrderItem[],
private status: OrderStatus,
private customerId: CustomerId
) {}
addItem(item: OrderItem): void {
if (this.status !== OrderStatus.DRAFT) {
throw new Error('確定済み注文には追加できません');
}
this.items.push(item);
}
confirm(): void {
if (this.items.length === 0) {
throw new Error('空の注文は確定できません');
}
this.status = OrderStatus.CONFIRMED;
}
}
Value Object(値オブジェクト)
同一性を持たず、属性の値で等価判定されるオブジェクト:
class Money {
constructor(
readonly amount: number,
readonly currency: Currency
) {
if (amount < 0) throw new Error('金額は0以上');
}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error('通貨が異なります');
}
return new Money(this.amount + other.amount, this.currency);
}
equals(other: Money): boolean {
return this.amount === other.amount && this.currency === other.currency;
}
}
Aggregate(集約)
トランザクション整合性の境界を定義する:
// Order集約 - OrderとOrderItemはセットでトランザクション管理
class OrderAggregate {
private order: Order;
private items: OrderItem[]; // Order内でのみ管理
// 集約ルート経由でのみ操作可能
addItem(productId: ProductId, quantity: number, price: Money): void {
this.order.addItem(new OrderItem(productId, quantity, price));
}
// 他の集約への参照はIDのみ(直接参照しない)
getCustomerId(): CustomerId {
return this.order.customerId; // IDのみ返す
}
}
集約設計の原則:
- 集約は小さく保つ
- 他の集約はIDで参照する(直接オブジェクト参照しない)
- 集約間の整合性は結果整合性(eventual consistency)を使う
DDDからサービス境界へ
マッピングルール
| DDD概念 | マイクロサービス |
|---|---|
| Bounded Context | 1つのマイクロサービス |
| Aggregate | サービス内のトランザクション境界 |
| Domain Event | サービス間の非同期通信 |
| Anti-Corruption Layer | サービス間のアダプター |
サービスサイズの目安
graph LR
Small["小さすぎ\nNano Service\n関数1つ"] --- Right["適切なサイズ\n2-pizza team"]
Right --- Big["大きすぎ\nモノリス\n全機能"]
Small -.- Issue1["通信オーバーヘッド大"]
Big -.- Issue2["変更影響大"]
style Small fill:#fee2e2,stroke:#dc2626,color:#991b1b
style Right fill:#d1fae5,stroke:#059669,stroke-width:3px,color:#065f46
style Big fill:#fee2e2,stroke:#dc2626,color:#991b1b
style Issue1 fill:#fef3c7,stroke:#d97706,color:#92400e
style Issue2 fill:#fef3c7,stroke:#d97706,color:#92400e
2-pizza team ルール: 1つのサービスを1チーム(5-8名)で所有・運用できるサイズが適切。
まとめ
| ポイント | 内容 |
|---|---|
| 戦略的DDD | サブドメイン分類で投資優先度を決定 |
| ユビキタス言語 | コンテキスト内で用語を統一する |
| 戦術的DDD | Entity/VO/Aggregate で境界を明確化 |
| サービス境界 | Bounded Context = マイクロサービス |
チェックリスト
- Core/Supporting/Generic サブドメインの分類を理解した
- ユビキタス言語の重要性を理解した
- Entity と Value Object の違いを説明できる
- Aggregate がトランザクション境界であることを理解した
次のステップへ
次は Bounded Context の特定とコンテキストマッピングを学びます。
推定読了時間: 40分