Backend for Frontend(BFF)とは、特定のフロントエンドのために用意する専用バックエンドです。Web、iOS、Android、外部連携などのクライアントが同じ汎用APIを直接使うのではなく、各クライアント向けのサーバーサイド層がマイクロサービスのデータを集約し、その画面に必要な形へ整形して返します。
Sam Newmanは、SoundCloudでの経験をもとに2015年にこのパターンを命名し、広めました。10年以上経った現在でも、BFFは複数クライアントの背後でマイクロサービスを運用するチームにとって有効な設計パターンであり、Microsoftも主要なクラウドアーキテクチャパターンとして文書化しています。
BFFが解決する問題
最初は、単一のWebアプリと単一のバックエンドで十分だったとします。バックエンドはREST APIを公開し、Webアプリがそれを呼び出すだけです。
しかし、その後に次のようなクライアントが増えると状況が変わります。
- モバイルアプリ
- パートナー連携
- スマートウォッチ用ウィジェット
- 管理画面
すべてのクライアントが同じバックエンドを使うと、バックエンドは全員の要求を同時に満たそうとして複雑になります。
主な問題は2つです。
1. 過剰フェッチと不足フェッチ
汎用エンドポイントは固定形式のレスポンスを返します。
たとえば、デスクトップのダッシュボードは次のような大きな顧客データを必要とするかもしれません。
{
"id": "cus_123",
"name": "Yamada Taro",
"orders": [],
"recommendations": [],
"accountSettings": {},
"notificationPreferences": {}
}
一方、モバイルの一覧画面では次の3項目だけで十分な場合があります。
{
"id": "cus_123",
"name": "Yamada Taro",
"avatarUrl": "https://example.com/avatar.png"
}
同じAPIを使うと、モバイルは不要なデータをダウンロードすることになります。これが過剰フェッチです。
逆に、必要な情報を集めるために複数APIを呼ばなければならない場合は不足フェッチになります。
2. クライアントが「おしゃべり」になる
バックエンドが画面単位に最適化されていない場合、クライアントが複数のAPIを呼び出してデータを組み立てる必要があります。
たとえば、モバイルのホーム画面で次の情報が必要だとします。
- プロフィール
- 通知数
- フィード
- おすすめコンテンツ
BFFがない場合、クライアントは次のような呼び出しを行うことになります。
GET /profile
GET /notifications/count
GET /feed
GET /recommendations
この方式では、ラウンドトリップが増え、レイテンシやバッテリー消費が増加します。また、オーケストレーションロジックがクライアント側に漏れ、テストやバージョン管理が難しくなります。
根本的な問題は技術だけではありません。共有バックエンドがすべてのフロントエンドチームの要求を受けると、変更のたびにチーム間調整が必要になり、バックエンドがボトルネックになります。
BFFパターンの仕組み
BFFは、1つのフロントエンドとダウンストリームサービスの間に置く薄いサーバーサイド層です。
[ Web app ] ---> [ Web BFF ] ---\
[ iOS app ] ---> [ iOS BFF ] -----> [ Microservices ]
[ Android app ] ---> [ Android BFF ] ---/
BFFは主に3つの役割を持ちます。
1. 集約
画面に必要な複数のマイクロサービスをBFFが呼び出し、1つのレスポンスにまとめます。
クライアント側では、次のように1回のリクエストで済みます。
GET /home
BFF内部では、必要に応じて複数サービスを並行呼び出しします。
app.get("/home", async (req, res) => {
const userId = req.user.id;
const [profile, notifications, feed] = await Promise.all([
profileService.getProfile(userId),
notificationService.getUnreadCount(userId),
feedService.getFeed(userId),
]);
res.json({
user: {
id: profile.id,
name: profile.name,
avatarUrl: profile.avatarUrl,
},
unreadNotifications: notifications.count,
feed,
});
});
これは、単一のユーザー体験に適用したAPI集約です。より一般的な考え方は、APIアグリゲーターパターンでも説明されています。
2. 整形
BFFは、クライアントが使いやすいレスポンス形状にデータを変換します。
例:
- 不要なフィールドを削除する
- フィールド名をUIに合わせる
- ネスト構造を平坦化する
- 日付や金額をクライアント向けにフォーマットする
モバイルBFFは軽量なペイロードを返し、デスクトップBFFは情報量の多いレスポンスを返せます。
3. 変換
BFFは、クライアント固有の技術的な都合も吸収できます。
例:
- モバイル向けのキャッシュ戦略
- ページネーション形式の変換
- プロトコル変換
- 画面単位のレスポンス最適化
重要なのは、ダウンストリームのマイクロサービスを汎用のまま保つことです。マイクロサービスはビジネス機能を提供し、BFFはクライアント固有の整形を担当します。
マイクロサービスとの関係を整理したい場合は、マイクロサービスとAPIの概要や、モノリスからマイクロサービスへの移行も参考になります。
クライアントエクスペリエンスごとに1つのBFF
Sam Newmanの基本方針は「1つのエクスペリエンスに1つのBFF」です。
たとえば、Web、iOS、Androidが大きく異なる体験を提供しているなら、それぞれにBFFを用意します。
web-bff
ios-bff
android-bff
1つのBFFで複数クライアントを処理しようとすると、次のような条件分岐が増えます。
if (client === "mobile") {
return lightweightPayload(data);
}
if (client === "web") {
return richPayload(data);
}
この状態が進むと、BFFは結局「全員向けの汎用バックエンド」に戻ってしまいます。
ただし、例外もあります。1つのチームがiOSとAndroidを所有しており、両者の体験やデータ要件がほぼ同じなら、単一のモバイルBFFを共有しても妥当です。
判断基準はプラットフォーム名ではなく、次の2つです。
- 所有チームが同じか
- クライアント体験が十分に似ているか
所有権はフロントエンドチームに置く
BFFは、プラットフォームチームが作って引き渡すだけの層ではありません。クライアントを所有するフロントエンドチームが、そのBFFも所有するのが基本です。
これにより、フロントエンドチームは次を自分たちで制御できます。
- リリースサイクル
- 実装言語とランタイム
- APIの優先順位
- UI変更とBFF変更の同時リリース
たとえば、新しい画面で集約APIが必要になったとき、別のバックエンドチームに依頼してキューを待つ必要がありません。画面を作るチームが、その画面に必要なBFFも変更できます。
この自律性がBFFの大きな価値です。クライアント固有の判断を、クライアントに責任を持つチームが行えるようになります。これは、API主導型コネクティビティにおける「エクスペリエンス層」の考え方とも一致します。
BFF vs APIゲートウェイ
BFFとAPIゲートウェイは、どちらもクライアントとサービスの間に置かれるため混同されやすいです。しかし、役割は異なります。
APIゲートウェイは、すべてのトラフィックに共通する横断的関心事を処理します。
例:
- 認証
- レート制限
- ルーティング
- TLS終端
- リクエストログ
- 監視
通常、APIゲートウェイはプラットフォームチームやインフラチームが所有し、クライアントから独立しています。
一方、BFFはクライアント固有です。
| 項目 | APIゲートウェイ | BFF |
|---|---|---|
| 主な目的 | 共通の入口、横断的処理 | クライアント向けの集約・整形 |
| 所有者 | プラットフォーム / インフラチーム | フロントエンドチーム |
| 対象 | 全クライアント共通 | 特定クライアント |
| 典型処理 | 認証、レート制限、ルーティング | ペイロード整形、画面単位の集約 |
両者は競合しません。一般的な本番構成では、APIゲートウェイを前段に置き、その背後に各BFFを配置します。
[ Clients ]
|
[ API Gateway ]
|
+--> [ Web BFF ]
+--> [ iOS BFF ]
+--> [ Android BFF ]
|
[ Microservices ]
共通の処理はゲートウェイに置き、クライアントごとに異なる処理はBFFに置きます。
関連する比較として、API管理 vs APIゲートウェイ、APIゲートウェイ vs ロードバランサー、サービスメッシュ vs APIゲートウェイも参考になります。
BFFを使うタイミング
BFFは、次の条件に当てはまる場合に効果を発揮します。
複数の異なるクライアントがある
Web、モバイル、パートナーAPIなどがそれぞれ異なるデータ要件を持つ場合、BFFで分離すると扱いやすくなります。
共有バックエンドがボトルネックになっている
フロントエンド変更のたびにバックエンドチームとの調整が必要なら、クライアント固有の整形をBFFへ切り出すことで調整コストを減らせます。
クライアントごとに最適化されたペイロードが必要
モバイルには軽量レスポンス、デスクトップにはリッチな集約データを返すなど、クライアント単位で最適化できます。
フロントエンドチームに適した技術スタックを選びたい
BFFはクライアントチームが所有するため、チームのスキルや運用に合うランタイムを選べます。
BFFを使わないタイミング
BFFは無料ではありません。次のケースでは、追加しない方がよいことがあります。
クライアントが1つしかない
単一のインターフェースだけなら、BFFは余分なネットワークホップになります。通常のバックエンドで十分です。
クライアントが同じデータを必要としている
Webとモバイルがほぼ同じリクエストとレスポンスを使うなら、別々のBFFは重複になります。
GraphQLがすでに問題を解決している
GraphQLでは、クライアントが必要なフィールドを1つのエンドポイントから取得できます。そのため、過剰フェッチや不足フェッチの多くはGraphQLで解決できます。
すでにフロントエンド固有のリゾルバーを持つGraphQL層があるなら、別のBFF層を追加しても価値が小さい場合があります。判断前に、GraphQLとは何かを確認してください。
APIゲートウェイとマイクロサービスで十分
システムが比較的シンプルなら、適切に設計されたマイクロサービスとAPIゲートウェイだけで十分な場合があります。
BFFのデメリット
BFFが適切な選択であっても、コストはあります。
コードの重複
複数のBFFがあると、同じような認証チェック、日付フォーマット、エラーハンドリングが重複しがちです。
対策は次の通りです。
- 本当に共通の処理は共有ライブラリに切り出す
- 認証、レート制限、監視などはAPIゲートウェイへ寄せる
- BFFにはクライアント固有の整形だけを残す
運用するサービスが増える
各BFFは独立したデプロイ単位です。
つまり、次が増えます。
- CI/CDパイプライン
- 監視
- アラート
- セキュリティ管理
- オンコール対象
BFFを増やす前に、運用コストも見積もるべきです。
ネットワークホップが増える
クライアントはマイクロサービスへ直接アクセスせず、BFFを経由します。そのため、BFF自体は1ホップ分の遅延を追加します。
ただし、多くの場合、BFFが複数のクライアントリクエストを1回にまとめるため、全体のレイテンシは改善します。
推測ではなく、実際のワークロードで測定してください。
BFFが肥大化する
BFFにビジネスロジックを入れすぎると、単なる新しいモノリスになります。
BFFに置くべきもの:
- クライアント向けの集約
- レスポンス整形
- 画面単位のオーケストレーション
BFFに置くべきでないもの:
- ドメインルール
- 永続化の中核ロジック
- 複数クライアント共通のビジネス機能
BFFは薄く保つことが重要です。
ApidogでBFFコントラクトを同期させる
BFFを運用するときに難しいのはAPIコントラクトです。
各BFFはクライアント向けAPIを公開し、同時に下位のマイクロサービスAPIにも依存します。レイヤーごとに所有チームが異なるため、コントラクトのずれがバグやクライアント破損につながります。
ここでApidogを使うと、BFFのAPIコントラクトを設計、モック、テスト、ドキュメント化する場所を一元化できます。
1. コントラクトを先に設計する
Apidogのビジュアルデザイナーで、各BFFエンドポイントのリクエストとレスポンススキーマをOpenAPIベースで定義します。
これにより、実装前にフロントエンドとバックエンドが同じAPI仕様を確認できます。
これはBFFに対するコントラクトファーストアプローチであり、APIコントラクトを明確に保つ方法です。
2. BFFが未実装でもモックで開発する
コントラクトが決まれば、フロントエンドチームはBFFの実装完了を待たずにモックAPIで開発できます。
開発フローの例:
- BFFのレスポンススキーマを定義する
- Apidogでモックを生成する
- フロントエンドはモックAPIに接続して画面を実装する
- BFF実装後に接続先を実APIへ切り替える
3. コントラクトをテストする
Apidogの自動テストとアサーションを使うと、BFFが期待どおりの集約済みレスポンスを返しているか検証できます。
CIに組み込めば、下位サービスの変更によってBFFレスポンスが壊れた場合も早期に検出できます。
4. 両側のチーム向けにドキュメント化する
Apidogはコントラクトからインタラクティブなドキュメントを生成できます。
これにより、次のチームが同じ仕様を参照できます。
- BFFを呼び出すフロントエンドチーム
- 下位サービスを所有するバックエンドチーム
- QAチーム
- API利用者
なお、ApidogはBFFを構築、ホスト、実行するものではありません。また、APIゲートウェイでもありません。
Apidogは、BFFの背後にあるAPIコントラクトを設計、モック、テスト、ドキュメント化するための場所です。各BFFを、安定したコントラクトを持つ製品として扱うことが、BFFパターンを持続可能にする鍵です。
実装時のチェックリスト
BFFを導入する前に、次を確認してください。
- [ ] クライアントごとに本当に異なるデータ要件がある
- [ ] BFFの所有チームが明確である
- [ ] 共通処理とクライアント固有処理の境界が決まっている
- [ ] APIゲートウェイとの責務分担が決まっている
- [ ] BFFのAPIコントラクトを管理する場所がある
- [ ] モックと自動テストを用意できる
- [ ] レイテンシと運用コストを測定する計画がある
よくある質問
BFFはマイクロサービスですか?
BFFはサーバーサイドサービスであり、マイクロサービス構成では通常その一部として動作します。
ただし、役割は一般的なマイクロサービスとは異なります。マイクロサービスはビジネス機能を所有し、クライアントから独立しています。BFFは1つのクライアント体験を所有し、そのクライアント向けにマイクロサービスを集約・整形します。
いくつのBFFを持つべきですか?
デフォルトは、明確に異なるクライアントエクスペリエンスごとに1つです。
例:
- Web用BFF
- iOS用BFF
- Android用BFF
ただし、1つのチームがほぼ同じ要件を持つ複数クライアントを所有している場合は、共有BFFでも構いません。
1つのBFFにクライアント別の条件分岐が増えてきたら、分割を検討してください。
GraphQLはBFFパターンを置き換えますか?
ペイロード整形に関しては、置き換えられる場合があります。
GraphQLでは、クライアントが必要なフィールドを正確に指定できるため、過剰フェッチや不足フェッチを減らせます。
ただし、次が必要な場合はBFFが有効です。
- クライアントごとの複雑なオーケストレーション
- プロトコル変換
- クライアントチームごとのランタイム選択
- GraphQL層では扱いにくい画面単位の処理
BFFとAPIゲートウェイを一緒に使えますか?
はい。一般的な構成です。
APIゲートウェイは認証、レート制限、監視などの共通処理を担当し、各BFFへルーティングします。BFFはクライアント固有の集約と整形を担当します。
BFFは誰が所有すべきですか?
クライアントを所有するフロントエンドチームです。
この所有権により、UI変更とBFF変更を同時に進められます。また、別のバックエンドチームのキューを待たずに、クライアント体験に必要なAPIを自分たちで進化させられます。
BFFはレイテンシを追加しますか?
はい。BFFを経由するため、1つのネットワークホップは追加されます。
ただし、BFFが複数のサービス呼び出しをサーバー側で並行実行し、クライアントからの複数リクエストを1回にまとめられる場合、全体のレイテンシは改善することがあります。
結論は構成によるため、実際に測定してください。

Top comments (0)