ストーリー
高橋アーキテクトがマイクロサービスの構成図を描いた。 ユーザーサービス、商品サービス、注文サービス…それぞれが独自のAPIを持っている。
API Gatewayとは
基本概念
graph LR
subgraph "Before(API Gateway なし)"
W1["Webアプリ"] --> U1["ユーザーサービス"]
W1 --> P1["商品サービス"]
W1 --> O1["注文サービス"]
M1["モバイルアプリ"] --> U1
M1 --> P1
M1 --> O1
end
subgraph "After(API Gateway あり)"
W2["Webアプリ"] --> GW["API Gateway"]
M2["モバイルアプリ"] --> GW
GW --> U2["ユーザーサービス"]
GW --> P2["商品サービス"]
GW --> O2["注文サービス"]
end
API Gatewayの役割
// 1. ルーティング
// クライアントのリクエストを適切なサービスに転送
// /api/users/* → ユーザーサービス
// /api/products/* → 商品サービス
// /api/orders/* → 注文サービス
// 2. 認証・認可
// すべてのリクエストの認証を一箇所で処理
// JWTトークンの検証、APIキーの検証
// 3. レート制限
// サービスごとではなく、Gateway で一元管理
// 4. ログ・モニタリング
// すべてのAPIリクエストを一箇所でログ記録
// 5. プロトコル変換
// クライアント: REST → Gateway → 内部サービス: gRPC
// 6. レスポンス集約
// 複数サービスの結果を1つのレスポンスに統合
実装例
// Express ベースの簡易 API Gateway
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();
// 認証ミドルウェア(全リクエストに適用)
app.use(authMiddleware);
// レート制限
app.use(rateLimitMiddleware({ windowMs: 60000, max: 100 }));
// ルーティング
app.use('/api/users', createProxyMiddleware({
target: 'http://user-service:3001',
pathRewrite: { '^/api/users': '/users' },
}));
app.use('/api/products', createProxyMiddleware({
target: 'http://product-service:3002',
pathRewrite: { '^/api/products': '/products' },
}));
app.use('/api/orders', createProxyMiddleware({
target: 'http://order-service:3003',
pathRewrite: { '^/api/orders': '/orders' },
}));
BFF(Backend for Frontend)
なぜBFFが必要か
// 問題: 異なるクライアントが異なるデータを必要とする
// Webアプリ(デスクトップ): 大きな画面、高速回線
// → ユーザー情報 + プロジェクト一覧 + 統計データ + 通知
// モバイルアプリ: 小さな画面、低速回線
// → ユーザーの名前とアバター + タスク3件 + 未読通知数
// IoTデバイス: 極小画面、超低速回線
// → タスク数だけ
// 1つのAPIで全クライアントに対応すると:
// - Webは必要なデータが足りない(Under-fetching)
// - モバイルは不要なデータが多い(Over-fetching)
BFFパターン
graph LR
Web["Webアプリ"] --> WBFF["Web BFF"]
WBFF --> US["ユーザーサービス"]
WBFF --> PS["商品サービス"]
WBFF --> OS["注文サービス"]
Mobile["モバイルアプリ"] --> MBFF["Mobile BFF"]
MBFF --> US
MBFF --> PS
IoT["IoTデバイス"] --> IBFF["IoT BFF"]
IBFF --> OS
BFFの実装
// Web BFF(デスクトップ向け)
// 豊富なデータを返す
app.get('/web/dashboard', async (req, res) => {
const [user, projects, stats, notifications] = await Promise.all([
userService.getUser(req.userId),
projectService.listProjects(req.userId, { limit: 10 }),
statsService.getDashboardStats(req.userId),
notificationService.getNotifications(req.userId, { limit: 20 }),
]);
res.json({
user: {
id: user.id,
name: user.name,
email: user.email,
avatarUrl: user.avatarUrl,
role: user.role,
},
projects,
stats,
notifications,
});
});
// Mobile BFF(モバイル向け)
// 必要最小限のデータを返す
app.get('/mobile/dashboard', async (req, res) => {
const [user, tasks, unreadCount] = await Promise.all([
userService.getUser(req.userId),
taskService.getMyTasks(req.userId, { limit: 3 }),
notificationService.getUnreadCount(req.userId),
]);
res.json({
user: {
name: user.name,
avatarUrl: user.avatarUrl,
},
recentTasks: tasks.map(t => ({
id: t.id,
title: t.title,
status: t.status,
})),
unreadNotifications: unreadCount,
});
});
API Gateway vs BFF
| 観点 | API Gateway | BFF |
|---|---|---|
| 目的 | リクエストのルーティングと横断的関心事 | クライアント固有のAPI提供 |
| 数 | 通常1つ | クライアントの種類ごとに1つ |
| ビジネスロジック | 含まない | クライアント向けの変換ロジックを含む |
| 管理者 | インフラチーム | フロントエンドチーム |
| 組み合わせ | BFFの前段に配置可能 | Gateway の後段に配置可能 |
組み合わせパターン
graph LR
Web["Webアプリ"] --> GW["API Gateway<br/>認証、レート制限、ログ"]
Mobile["モバイルアプリ"] --> GW
IoT["IoTデバイス"] --> GW
GW --> WBFF["Web BFF"]
GW --> MBFF["Mobile BFF"]
GW --> IBFF["IoT BFF"]
WBFF --> SVC1["サービス群"]
MBFF --> SVC1
IBFF --> SVC1
style GW fill:#f9f,stroke:#333
まとめ
| パターン | 役割 | 適する場面 |
|---|---|---|
| API Gateway | ルーティング、認証、レート制限 | マイクロサービスの一元管理 |
| BFF | クライアント固有のAPI提供 | 複数クライアント向けAPI |
| 組み合わせ | 横断的関心事 + クライアント最適化 | 大規模システム |
チェックリスト
- API Gatewayの役割(ルーティング、認証、レート制限)を理解した
- BFFパターンが必要な理由を説明できる
- API GatewayとBFFの違いを説明できる
- 組み合わせパターンの構成を理解した
次のステップへ
API GatewayとBFFを学びました。
次は演習です。APIバージョン移行計画を実際に立ててみましょう。
推定読了時間: 30分