BFF (Backend for Frontend)

フロントエンドごとに専用のバックエンドを用意し、クライアントに最適化された API を提供するパターン

アーキテクチャAPI

BFF とは

BFF (Backend for Frontend) は、フロントエンドごとに専用のバックエンドを用意し、クライアントに最適化された API を提供するパターンである。Sam Newman が提唱。詳細は「BFF パターン」を参照。

なぜ必要か

マイクロサービス環境では、モバイルアプリと Web アプリで必要なデータの形式や量が異なる。汎用 API を 1 つだけ用意すると、モバイルには不要なフィールドまで返却され、通信量が増える。BFF を設けることで、各クライアントに最適化されたレスポンスを返せる。

❌ 汎用 API:
  モバイル: /api/users/123 → 50 フィールド返却 (大半は不要)
  Web: /api/users/12350 フィールド返却 (別のフィールドが不要)

✅ BFF:
  モバイル → Mobile BFF → { name, avatar } (必要最小限)
  WebWeb BFF → { name, email, orders } (Web に最適化)

アーキテクチャ

各フロントエンドに専用の BFF を配置し、背後のマイクロサービス群から必要なデータだけを集約して返す。BFF はクライアントごとのデータ整形に専念し、ビジネスロジックは持たない。

モバイルアプリ → Mobile BFF (Lambda) → マイクロサービス群
Web アプリ    → Web BFF (Lambda)    → マイクロサービス群
管理画面      → Admin BFF (Lambda)  → マイクロサービス群

BFF vs 汎用 API vs GraphQL

BFF と汎用 API vs GraphQL の違いを以下にまとめる。

観点汎用 APIBFFGraphQL
Over-fetching発生する解決解決
クライアント最適化全クライアント共通の形で返す画面ごとに必要な形へ整えて返すクライアントが必要な項目を指定する
バックエンド数1クライアント数分1
複雑さ低い中〜高

Lambda での BFF

Lambda での BFF のコード例を示す。

// Mobile BFF: モバイルに最適化されたレスポンス
export const mobileHandler = async (event: APIGatewayProxyEventV2) => {
  const userId = event.pathParameters?.id;
  const [user, orders] = await Promise.all([
    userService.get(userId!),
    orderService.getRecent(userId!, 3), // モバイルは直近 3 件のみ
  ]);
  return {
    statusCode: 200,
    body: JSON.stringify({
      name: user.name,
      avatar: user.avatar,
      recentOrders: orders.map(o => ({ id: o.id, total: o.total })),
    }),
  };
};

BFF の注意点

BFF の注意点を以下にまとめる。

注意点対策
BFF の肥大化ビジネスロジックを BFF に入れない
重複コード共通ロジックをライブラリに抽出
BFF の数が増えるGraphQL で代替を検討

いつ BFF を使うか

クライアントの数だけバックエンドを増やす判断は、レスポンスの形が画面ごとにどれだけ違うかで決まる。

場面判断理由
モバイルと Web で必要なフィールドが食い違うBFF を置く汎用 API では両方の要求を足した形になり、モバイル側は使わないフィールドまで受け取って通信量が増える
フロントエンドが 1 つだけ汎用 API のまま整形先が 1 つなら層を挟んでも返す形は変わらず、デプロイ対象と監視対象が増えるだけになる
クライアント側が必要なフィールドを都度決めたいGraphQL を検討取得したい形をクエリで指定できるため、画面が増えるたびに BFF を作り足す運用を避けられる

より深く学ぶには関連書籍が役立つ。

この記事は役に立ちましたか?

関連用語

関連する記事