Skip to content
Published on

GraphQL Federation 完全ガイド 2025: Apollo Federation, Supergraph, 分散 GraphQL

Authors

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 箇所で管理される。
  2. 巨大なコードベース: 全ドメインが 1 サーバ。
  3. デプロイの結合: 1 ドメイン変更 = 全体再デプロイ。
  4. スケーラビリティ: 単一障害点。

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

  1. スキーマ分析 — ドメインごとに分類。
  2. 最初の Subgraph 抽出 (最も明確なドメイン、例: users)。Router をモノリスの前段に置く。
  3. 段階的に分離 — 1 ドメインずつ。
  4. モノリスを廃止。

8.2 REST から Federation

  1. 各 REST API を GraphQL Subgraph としてラップ。
  2. ラッパー Subgraph を Federation。
  3. ネイティブ 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 に不可欠。


参考資料