APIゲートウェイとBFFのコアな違いを理解する
Ethan Miller
Product Engineer · Leapcell

はじめに
現代のソフトウェアアーキテクチャ、特にマイクロサービスの普及に伴い、サービスの複雑な網を管理し、フロントエンドとバックエンド間の通信を最適化することが最重要になっています。この課題に関する議論で頻繁に登場するアーキテクチャパターンが2つあります:APIゲートウェイとBackend for Frontend(BFF)です。どちらも仲介者として機能するように見えますが、その目的、機能、意図されたユースケースは根本的に異なります。これらの違いを誤解すると、最適ではないアーキテクチャの選択、複雑さの増大、開発速度の低下につながる可能性があります。この記事では、KongのようなツールのAPIゲートウェイとBFFパターンとの根本的な違いを明確にし、適切に設計されたシステムにおいて、なぜ両方が重要な役割を果たすのかを、異なる理由で説明することを目的としています。
中間層の解剖
コアな違いに飛び込む前に、関連する主要な用語を明確に理解しましょう。
**APIゲートウェイ:**APIゲートウェイは、定義されたマイクロサービスのセットに対する単一の入り口として機能するサーバーです。複数のクライアントが必要とする共通の機能を集約し、適切なバックエンドサービスにリクエストを転送します。マイクロサービスのための「交通整理係」と考えてください。リクエストが個々のサービスに到達する前に、共通の懸念事項を処理します。
**Backend for Frontend(BFF):**BFFパターンは、特定のフロントエンドアプリケーションまたはユーザーエクスペリエンスごとに専用のバックエンドサービスを作成することを含みます。すべてのクライアントに単一のモノリシックAPIを提供するのではなく、各フロントエンド(例:Webアプリ、iOSアプリ、Androidアプリ)は独自のカスタマイズされたバックエンドを取得します。このバックエンドは、対応するフロントエンドの特定のニーズに合わせて、さまざまな下流のマイクロサービスへの呼び出しを調整(オーケストレーション)し、データを変換および集約します。
APIゲートウェイ:インフラストラクチャ層
APIゲートウェイは主にインフラストラクチャレベルで機能します。そのコアな責任は広範かつ汎用的であり、クライアントに関係なくすべて受信リクエストに適用されます。
原則:
- **集中的なリクエスト処理:**すべてのアウトバウンドリクエストはゲートウェイを経由します。
 - **共通の懸念事項:**認証、認可、レート制限、トラフィック管理、ルーティング、ロードバランシング、キャッシング、分析を処理します。
 - **サービスディスカバリ:**正しいマイクロサービスインスタンスを見つけてリクエストをルーティングします。
 - **プロトコル変換:**異なるプロトコル(例:RESTからgRPC)を変換できます。
 
実装(Kongを使用した概念):
UserService、ProductService、OrderService といういくつかのマイクロサービスがあると想像してください。APIゲートウェイがない場合、フロントエンドは各サービスのIPアドレスとポートを知る必要があり、共通の懸念事項を手動で処理する必要があります。
Kongを使用すると、受信リクエストをサービスにマッピングするルートを定義できます。
# kong.yml _format_version: "2.1" services: - name: user-service url: http://user-service:8080 routes: - name: user-route paths: - /users strip_path: true - name: product-service url: http://product-service:8081 routes: - name: product-route paths: - /products strip_path: true plugins: - name: jwt # 例:JWT認証をグローバルに適用 service: user-service config: claims_to_verify: ["exp"]
この例では、Kongは /users を user-service に、 /products を product-service にルーティングします。また、JWT認証のようなプラグインをすべてのサービスまたはルートに普遍的に適用し、個々のマイクロサービスからこの責任をオフロードすることもできます。
アプリケーションシナリオ:
- **マイクロサービスの外部公開:**外部クライアントに単一の安全なエントリポイントを提供します。
 - **APIバージョンの管理:**さまざまなバージョンのサービスにリクエストをルーティングできます。
 - **セキュリティ実施:**集中的な認証、認可、脅威保護。
 - **監視と分析:**APIの使用状況とパフォーマンスに関するメトリックを収集します。
 
BFF:クライアントに合わせた層
一方、BFFパターンは、特定のクライアントのエクスペリエンスを最適化することに焦点を当てたデザインパターンです。インフラストラクチャ中心ではなく、クライアント中心です。
原則:
- **クライアント固有のAPI:**各フロントエンドは、そのニーズに正確に合わせたAPIを受け取ります。
 - **データ集約と変換:**複数の下流サービスからのデータを、単一の最適化されたレスポンスに結合します。
 - **クライアントサイドの複雑さの軽減:**フロントエンドからデータ操作とオーケストレーションをオフロードします。
 - **独立したデプロイ:**BFFは、他のBFFやコアマイクロサービスとは独立して進化し、デプロイできます。
 
実装(Web UI向けのNode.jsでの概念):
Webアプリケーションが、 recent_orders や favorite_products を含むユーザープロフィールを表示する必要があるシナリオを考えてみましょう。このデータは UserService、OrderService、ProductService から取得されます。
// web-bff/src/index.js const express = require('express'); const axios = require('axios'); const app = express(); const port = 3001; // これらは内部マイクロサービスURLであると想定。内部的にAPIゲートウェイ経由でルーティングされる可能性あり const USER_SERVICE_URL = 'http://user-service:8080'; const ORDER_SERVICE_URL = 'http://order-service:8082'; const PRODUCT_SERVICE_URL = 'http://product-service:8081'; app.get('/api/web/user-dashboard/:userId', async (req, res) => { try { const userId = req.params.userId; // ユーザー詳細の取得 const userRes = await axios.get(`${USER_SERVICE_URL}/users/${userId}`); const userData = userRes.data; // ユーザーの直近の注文の取得 const ordersRes = await axios.get(`${ORDER_SERVICE_URL}/orders?userId=${userId}&limit=5`); const recentOrders = ordersRes.data; // ユーザーのお気に入り製品の取得(製品IDがユーザーデータに含まれていると想定) const productIds = userData.favoriteProductIds || []; const productPromises = productIds.map(productId => axios.get(`${PRODUCT_SERVICE_URL}/products/${productId}`) ); const productResponses = await Promise.all(productPromises); const favoriteProducts = productResponses.map(response => response.data); // 単一のWebクライアントビューのためにデータを集約および変換 const dashboardData = { user: { id: userData.id, name: userData.name, email: userData.email, }, recentOrders: recentOrders.map(order => ({ orderId: order.id, totalAmount: order.total, status: order.status })), favoriteProducts: favoriteProducts.map(product => ({ productId: product.id, name: product.name, price: product.price })) }; res.json(dashboardData); } catch (error) { console.error('ユーザーダッシュボードデータの取得中にエラーが発生しました:', error.message); res.status(500).json({ message: 'ダッシュボードデータの取得に失敗しました' }); } }); app.listen(port, () => { console.log(`Web BFF is listening at http://localhost:${port}`); });
ここでは、web-bff の /api/web/user-dashboard/:userId エンドポイントが複数のマイクロサービスからデータを集約し、Webインターフェイスに特化してレスポンスを調整し、フロントエンドでのボイラープレートコードを削減し、ネットワーク呼び出しを最適化する可能性があります。
アプリケーションシナリオ:
- 複数のクライアントタイプ:(例:モバイル、Web、スマートTV)のように、大幅に異なるクライアントアプリケーションがあり、それぞれ異なるデータ構造や詳細レベルを必要とする場合。
 - **複雑なUIデータ集約:**単一のUI画面が多くのバックエンドサービスからのデータ inorder 複数の、逐次API呼び出しを行うのを防ぐ。)
 - **進化するフロントエンド:**フロントエンドは、他のクライアントやコアバックエンドサービスに影響を与えることなく、より迅速にイテレーションおよびデプロイできます。
 - **クライアントごとのセキュリティ:**各クライアントの特定の権限に合わせた、きめ細かなアクセス制御。
 
根本的な違い
根本的な違いは、責任の範囲と推進力にあります。
- 
**APIゲートウェイはサービス中心です:**すべてのクライアントがバックエンドサービス全体と対話するための、安定した安全でパフォーマンスの高いエントリポイントを提供することに焦点を当てています。その懸念事項は主に水平的(横断的)であり、インフラストラクチャ関連です。特定のフロントエンドの表示ニーズを知ることも、気にかけることもありません。その目標は、効率的で安全なAPIの公開です。
 - 
**BFFはクライアント中心です:**特定のフロントエンドアプリケーションとの対話を最適化することに焦点を当てています。その懸念事項は垂直的(クライアント固有)であり、アプリケーション関連です。関連するクライアントのUI/UX要件を理解し、クライアントサイドの複雑さとネットワーク呼び出しを削減するためにデータを集約および変換する専門的なアダプターとして機能します。
 
両方を使用することも、よく推奨されます。APIゲートウェイは、すべてのBFF(およびその他の直接的なマイクロサービス)の前に配置され、受信リクエストの共通の認証やレート制限のような最初のレイヤーの共通の懸念事項を処理する可能性があります。その後、リクエストは適切なBFFに転送され、BFFはさらに深いマイクロサービスへの呼び出しを調整します。
結論
APIゲートウェイとBFFはどちらもマイクロサービスアーキテクチャにおいて重要な仲介者として機能しますが、それぞれ異なる目的を果たします。APIゲートウェイは、エコシステム全体の共通のエントリポイントと横断的な懸念事項を管理する、堅牢で共有されたインフラストラクチャ層です。Backend for Frontendは、個々のフロントエンドアプリケーションに最適化されたAPIエクスペリエンスを細心の注意を払って作成する、クライアント駆動の専門的な層です。この根本的な違いを理解することは、スケーラブルで保守可能でパフォーマンスの高いシステムを設計するために不可欠であり、多様なクライアントのニーズに効果的に対応します。これらは代替ではなく、補完的なパターンであり、回復力があり適応性のあるアプリケーションを構築するために連携して機能します。