Chuyển đến nội dung chính

第 19 課:GraphQL Federation — 微前端的統一 API

Apollo Federation:超級圖、子圖、路由器。每個微服務都公開 GraphQL 子圖。 Router組成統一的API。模式拼接與聯合。性能考慮。

🏗️ 建築 — 第 19 課 第 19 課:GraphQL Federation — 統一 API 對於微前端

微服務與微前端系統設計-從基礎到生產

第 6 部分:API 閘道和 BFF 層

亞洲開發網

簡介

GraphQL Federation允許每個微服務公開一個子圖,而Router會自動將其組合成統一的超級圖。前端僅需要 1 個端點來查詢每個服務的資料。

GraphQL Federation — 來自多個子圖的統一圖


1. 問題:多個 GraphQL 端點

❌ Mỗi service có GraphQL endpoint riêng:
Frontend → product.api.com/graphql (Product schema)
Frontend → user.api.com/graphql (User schema)
Frontend → order.api.com/graphql (Order schema)

→ Frontend phải biết endpoint nào chứa data gì
→ Không thể query cross-service trong 1 request
→ Ví dụ: Order + Product + User = 3 requests

2. GraphQL 聯邦架構

✅ Unified Supergraph:

┌─────────────┐
│   Frontend  │
│  1 endpoint │
└──────┬──────┘
       │
       ▼
┌──────────────────────┐
│   Apollo Router      │
│   (Supergraph)       │
│   gateway.api.com    │
└──┬────────┬────────┬─┘
   │        │        │
   ▼        ▼        ▼
┌──────┐ ┌──────┐ ┌──────┐
│Product│ │ User │ │Order │
│Subgra│ │Subgra│ │Subgra│
│  ph  │ │  ph  │ │  ph  │
└──────┘ └──────┘ └──────┘

1 query → Router splits → Subgraphs → Router merges → 1 response

3. 子圖定義

3.1 產品子圖

# Product Service subgraph
type Product @key(fields: "id") {
  id: ID!
  name: String!
  price: Float!
  description: String
  category: Category!
}

type Category {
  id: ID!
  name: String!
}

type Query {
  product(id: ID!): Product
  products(limit: Int, offset: Int): [Product!]!
}

3.2 回顧子圖(擴展產品)

# Review Service subgraph — extends Product
type Product @key(fields: "id") {
  id: ID!
  reviews: [Review!]!
  averageRating: Float
}

type Review {
  id: ID!
  rating: Int!
  comment: String
  author: User!
}

type Query {
  reviews(productId: ID!): [Review!]!
}

3.3 聯合查詢

# Frontend query — Router handles cross-service resolution
query ProductPage($id: ID!) {
  product(id: $id) {
    id              # → Product Subgraph
    name            # → Product Subgraph
    price           # → Product Subgraph
    reviews {       # → Review Subgraph
      rating
      comment
      author {      # → User Subgraph
        name
        avatar
      }
    }
    averageRating   # → Review Subgraph
  }
}

自動路由器: 1.查詢產品子圖→取得產品數據 2.查詢評論子圖→取得評論(使用product.id) 3.查詢使用者子圖→取得作者資訊(使用review.author.id) 4. 合併全部→回傳1個回應


4. 模式拼接與聯合

模式拼接聯合會
所有權網關擁有架構服務自己的架構
聯軸器高(網關了解服務)低(服務聲明本身)
可擴充性網關瓶頸分散式
進化硬(中心變化)簡單(服務等級)
判決❌ 遺產✅ 推薦

5. 效能注意事項

5.1 查詢計劃最佳化

Router tạo query plan tối ưu:
─ Parallel: Product + User subgraphs (independent)
─ Sequential: Reviews → after Product (needs product.id)

5.2 資料載入器模式

// Trong Review Subgraph, batch user lookups
const userLoader = new DataLoader(async (userIds) => {
  const users = await userService.getUsers(userIds);
  return userIds.map(id => users.find(u => u.id === id));
});

// Resolve author field
Review: {
  author: (review) => userLoader.load(review.authorId)
}

5.3 快取

Persisted Queries: Client gửi query hash thay vì full query
Automatic Persisted Queries (APQ): Router cache query plans
CDN caching: @cacheControl directive

6. 何時使用聯邦?

✅ Dùng khi:
- Nhiều microservices cần unified GraphQL API
- Frontend teams muốn 1 endpoint
- Complex, nested data relationships
- Multiple frontend clients

❌ Không cần khi:
- Chỉ có 1-2 services (đơn giản quá)
- REST đã đủ tốt
- Team chưa quen GraphQL
- Performance-critical (thêm latency qua Router)

總結

  • 聯邦 = 每個服務都有自己的子圖,Router組成超圖
  • @key 跨子圖的實體引用指令
  • 路由器自動分割、解析、合併查詢
  • DataLoader 模式避免了 N+1 問題
  • 當您需要統一的 GraphQL API 用於多種服務時使用

下一篇文章: 第 20 課:測試微服務 — 單元、整合與 E2E