TL;DR
- Federation: 複数の GraphQL サービスを単一の Supergraph に統合。マイクロサービス環境の標準。
- Schema Stitching の後継: Federation v2 が stitching を置き換え。より強力で明確。
- Subgraph 設計: ドメイン単位で分割。Entity で Subgraph 間のデータ共有。
- Apollo Router: Rust 実装。Gateway より 10 倍高速。CNCF 標準に。
- Netflix、Airbnb、Expedia、GitHub が Federation を採用。
1. なぜ GraphQL Federation が必要か
1.1 モノリシック GraphQL の限界
[Client]
↓
[GraphQL Server]
↓
[User Service] [Order Service] [Product Service] [...]
課題:
- 単一チームのボトルネック: スキーマが 1 箇所で管理される。
- 巨大なコードベース: 全ドメインが 1 サーバ。
- デプロイの結合: 1 ドメイン変更 = 全体再デプロイ。
- スケーラビリティ: 単一障害点。
1.2 Schema Stitching (以前の試み)
複数の GraphQL スキーマを 1 つにまとめる方式。
限界: 手動での衝突解決、複雑な設定、性能問題、明確な契約の欠如。
1.3 Federation の登場
Apollo Federation v1 (2019): Schema Stitching の進化形。 Federation v2 (2022): より明確な設計、より強力。
[Apollo Router]
/ | \
[Users] [Orders] [Products] ← 各 Subgraph は独立
要点: 各チームが自ドメインの GraphQL サービスを運営。Router が自動合成。
2. Federation のコア概念
2.1 Subgraph
Subgraph = Federation の単位。独自の GraphQL スキーマ。
# users-subgraph
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID!): User
users: [User!]!
}
@key: 他の Subgraph から参照可能にする。
2.2 Entity
Entity = 複数の Subgraph 間で共有される型。@key で識別。
users-subgraph:
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
orders-subgraph:
type Order @key(fields: "id") {
id: ID!
total: Float!
user: User! # User Entity を参照
}
type User @key(fields: "id") {
id: ID! @external
orders: [Order!]! # 新規フィールド追加
}
同じ User 型に、2 つの Subgraph が異なるフィールドを提供。
2.3 Reference Resolver
他の Subgraph から Entity が参照されたとき、データを解決する方法:
// users-subgraph
const resolvers = {
User: {
__resolveReference(reference) {
// reference = { id: "123" }
return getUserById(reference.id)
}
}
}
2.4 Composition
Router が全 Subgraph スキーマを受け取り、Supergraph schema を生成。クライアントは単一の Supergraph schema のみを見る。
3. 実例 — E コマース
3.1 Subgraph 設計
Users:
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
address: Address!
}
type Address {
street: String!
city: String!
zipcode: String!
}
Products:
type Product @key(fields: "id") {
id: ID!
name: String!
description: String!
price: Float!
stock: Int!
}
Reviews:
type Review @key(fields: "id") {
id: ID!
rating: Int!
comment: String!
productId: ID!
authorId: ID!
}
type Product @key(fields: "id") {
id: ID! @external
reviews: [Review!]!
averageRating: Float!
}
type User @key(fields: "id") {
id: ID! @external
reviews: [Review!]!
}
3.2 クライアントクエリ
query {
user(id: "123") {
name # users-subgraph
email # users-subgraph
reviews { # reviews-subgraph
rating
comment
product { # products-subgraph
name
price
}
}
}
}
Router が各 Subgraph からフィールドを取得し、単一のレスポンスに合成。
4. Apollo Router — 新しい標準
4.1 Gateway から Router へ
- Apollo Gateway (Node.js, 2019): 初期実装、シングルスレッド、メモリ消費大。
- Apollo Router (Rust, 2022): Rust で書き直し、10 倍以上高速、メモリ効率良。
4.2 インストール
curl -sSL https://router.apollo.dev/download/nix/latest | sh
./router --config router.yaml --supergraph supergraph.graphql
router.yaml:
supergraph:
listen: 0.0.0.0:4000
cors:
origins:
- https://example.com
telemetry:
metrics:
prometheus:
enabled: true
tracing:
otlp:
enabled: true
endpoint: http://otel-collector:4317
4.3 Schema Composition
rover supergraph compose --config supergraph-config.yaml > supergraph.graphql
federation_version: 2
subgraphs:
users:
routing_url: http://users-service:4001
schema:
subgraph_url: http://users-service:4001
products:
routing_url: http://products-service:4002
schema:
subgraph_url: http://products-service:4002
reviews:
routing_url: http://reviews-service:4003
schema:
subgraph_url: http://reviews-service:4003
4.4 GraphOS — Apollo のマネージド
Schema registry (自動 composition)、schema validation (CI 連携)、operation 分析、性能モニタリング、schema diff (破壊的変更の検出)。
rover subgraph check my-graph@prod \
--schema users.graphql \
--name users
5. Federation ディレクティブ
5.1 @key
type User @key(fields: "id") {
id: ID!
name: String!
}
# 複合キー
type Order @key(fields: "id userId") {
id: ID!
userId: ID!
total: Float!
}
# 複数キー
type Product
@key(fields: "id")
@key(fields: "sku") {
id: ID!
sku: String!
name: String!
}
5.2 @external
他の Subgraph が定義するフィールドを参照。
type User @key(fields: "id") {
id: ID! @external
reviews: [Review!]!
}
5.3 @requires
他の Subgraph のフィールドが必要な場合。
type Product @key(fields: "id") {
id: ID! @external
weight: Float! @external
shippingCost: Float! @requires(fields: "weight")
}
5.4 @provides
この Subgraph が直接提供できるフィールドを宣言。余計な呼び出しを削減。
type Review @key(fields: "id") {
id: ID!
product: Product! @provides(fields: "name")
}
type Product @key(fields: "id") {
id: ID! @external
name: String! @external
}
5.5 @shareable (v2)
複数の Subgraph が同じフィールドを定義できる。
5.6 @inaccessible
特定のフィールドを Supergraph から隠す (内部専用)。
6. よくあるパターン
6.1 ドメイン分割
誤り: 技術別 (DB / Cache / Auth Subgraph)。 正解: ビジネスドメイン別 (Users / Orders / Products / Inventory / Shipping)。
6.2 Entity の所有権
各 Entity は必ず 1 つの「owner」Subgraph を持つ。所有側が identity を定義し、他の Subgraph は追加フィールドで拡張するだけ。
6.3 N+1 対策
DataLoader パターンで Entity 取得をバッチ化:
const orderLoader = new DataLoader(async (userIds) => {
const orders = await db.query('SELECT * FROM orders WHERE user_id IN (?)', userIds)
return userIds.map(id => orders.filter(o => o.user_id === id))
})
const resolvers = {
User: {
orders: (user) => orderLoader.load(user.id)
}
}
6.4 認証・認可
Router で JWT を検証し、ヘッダを Subgraph に伝播。
authentication:
router:
jwt:
jwks:
- url: https://auth.example.com/.well-known/jwks.json
headers:
all:
request:
- propagate:
named: authorization
7. 運用とモニタリング
7.1 メトリクス
Apollo Router は Prometheus メトリクスを標準提供: apollo_router_http_requests_total, apollo_router_http_request_duration_seconds など。
7.2 分散トレーシング
telemetry:
tracing:
otlp:
enabled: true
endpoint: http://otel-collector:4317
propagation:
trace_context: true
jaeger: true
Router から Subgraph への呼び出しが同じ trace に含まれる。
7.3 Schema Registry
CI で破壊的変更や composition の衝突を事前検出。本番に入る前に防ぐ。
7.4 Persisted Queries
クライアントはクエリ ID (ハッシュ) のみ送信。Router は事前登録されたクエリしか実行しない。任意クエリを遮断し、ペイロードを削減。
8. 移行戦略
8.1 モノリシック GraphQL から Federation
- スキーマ分析 — ドメインごとに分類。
- 最初の Subgraph 抽出 (最も明確なドメイン、例: users)。Router をモノリスの前段に置く。
- 段階的に分離 — 1 ドメインずつ。
- モノリスを廃止。
8.2 REST から Federation
- 各 REST API を GraphQL Subgraph としてラップ。
- ラッパー Subgraph を Federation。
- ネイティブ GraphQL へ段階的に移行。
8.3 よくある落とし穴
- 分割しすぎ: 5 人のドメインを 10 Subgraph に = 運用地獄。Conway の法則に従う。
- Entity 所有権の曖昧化: 単一 owner を決定。
- 同期依存: DataLoader とキャッシングで連鎖障害を防止。
9. 実際の事例
- Netflix: 数百の Subgraph、Java + DGS framework、schema-first、BFF パターン。
- Airbnb: モノリスから Federation へ。教訓 — 段階的な移行、一気にやらない。
- Expedia: 旅行検索の巨大システム、50+ Subgraph、DataLoader を広範に利用。
- GitHub: Public GraphQL API で Federation を内部採用。
10. Federation vs 代替案
10.1 モノリス vs Federation
| モノリス | Federation | |
|---|---|---|
| 複雑度 | 低 | 高 |
| チーム自律性 | 低 | 高 |
| スケーラビリティ | 単一ノード | 無制限 |
| 開発速度 | 小チーム速い | 大チーム速い |
| 運用 | 単純 | 複雑 |
目安: 5 人未満ならモノリス、それ以上なら Federation を検討。
10.2 Federation vs BFF
BFF = クライアント別 GraphQL サーバ (web / mobile / partner)。Federation = 単一 Supergraph。組合せ可能 — Federation + クライアント別 view。
10.3 Federation vs gRPC
典型パターン: Client ↔ Apollo Router (GraphQL)、Router ↔ Subgraph (GraphQL)、Subgraph ↔ 内部サービス (gRPC)。
クイズ
1. Schema Stitching と Federation の違いは?
答え: Stitching は手動で衝突を解決し、明確な契約がなかった。Federation は @key, @external による明示的な契約、自動 composition、破壊的変更の自動検出を備える。Federation v2 が Stitching の欠点を解消し、事実上の標準に。Stitching は deprecated。
2. @key ディレクティブの役割は?
答え: Entity を定義する。@key(fields: "id") は「この型は id で識別可能で、他の Subgraph から参照可能」を意味。Router は entity reference を受けると __resolveReference を呼ぶ。複合キー (@key(fields: "id userId")) や複数キーも可能。Federation で最も重要なディレクティブ。
3. Apollo Router が Gateway より速い理由は?
答え: Rust で書き直されたから。Gateway は Node.js 基盤 (シングルスレッド、メモリ消費大)。Router は Rust の zero-cost abstraction、Tokio async runtime (真のマルチスレッド)、メモリ効率で、10 倍以上高速、メモリ半分以下。Gateway は deprecated。
4. Subgraph 分割の正しい基準は?
答え: ビジネスドメイン別。誤: DB, Cache, Auth (技術別)。正: Users, Orders, Products, Inventory, Shipping。Conway の法則に従いチーム構造と一致させる。小分け過ぎ (5 人で 10 Subgraph) も大きすぎ (50 人で単一 Subgraph) も避ける。
5. Federation で N+1 をどう防ぐ?
答え: DataLoader パターン。同じ tick の複数 Entity 要求を 1 クエリに集約。Router は Entity reference をバッチ送信、Subgraph は DataLoader で一括処理。加えて @provides で cross-subgraph 呼び出しを削減、Redis などのキャッシュ、operation 分析による最適化。DataLoader は GraphQL に不可欠。
参考資料
현재 단락 (1/281)
- **Federation**: 複数の GraphQL サービスを単一の Supergraph に統合。マイクロサービス環境の標準。