Нужен ли вам BFF? Почему один бэкенд не может обслужить все прихоти пользователей

Архитектура

Классическое взаимодействие через RESTful-API прекрасно, но не лишено недостатков. Часто из огромного джейсона приходится выковыривать крупицы данных. Или напротив, из собирать воедину ответ от трех-четырех ручек. Уверен, вы с таким сталкивались. Побороться с этим призвано такое архитектурное решение (или даже паттерн) как BFF. Сегодня мы разберемся, зачем нужен еще один слой между фронтом и бэком.

Гастрономическая аналогия

В какой-то из статей я что-то объяснял через ларек с шаурмой. Сегодня мы вернемся к этому утопическому примеру. Итак, вы открыли лучшую шаурму на районе. В начале работы вы принимаете деньги сами, крутите шаурму сами, упаковываете в целофановые пакеты и выдаете клиентам через маленькое окошечко.

Бизнес идет в гору и вы открываете доставку. Рецепт шаурмы под нее вы не меняете, но вот упаковка — уже другая — например, алюминиевые боксы с картонной крышкой. В целофановом пакете она до покупателя не доедет. И сложность вашей работы кратко возрастает. Но сил пока хватает и вы делаете столики для того, чтобы клиенты могли употребить продукт на месте и с комфортом. Здесь вы выдаете блюда уже на тарелках и, может быть даже с приборами.

Переводя на язык программирования, вы только что совершили классическую ошибка в архитектуре апи. Наплодили разных методов, которые выполняют одно и то же, но выдают результат под разным соусом. Если перепоручить эту задачу кому-то другому, то можно сделать и бэкенд (приготовление шаурмы) и фронтенд (выдачу заказов) архитектурно чище и стабильнее.

В утопическом мире с ларьком шаурмы можно, например, нанять администратора, который будет упаковывать готовый продукт. А на проекте придется завести BFF.

По-простому. Что такое BFF

BFF — это отдельный сервис (или слой), который сидит между вашим фронтендом и основным бэкендом. Он работает так:

  1. Принимает запрос от фронта
  2. Идет в бэкенд-сервисы (в один или несколько одновременно), забирает данные
  3. Агрегирует и трансформирует данные под нужды конкретного фронта
  4. Отдает готовый ответ фронту в нужном формате.

BFF не содержит бизнес-логики. Он не считает скидки, не проверяет баланс, не сохраняет данные. Всю серьезную работу делает основной бэкенд. BFF — это что-то вроде переводчика или сборщика.

Проблемы, которые решает BFF

Проблема №1: Оверфетчинг (передача лишних данных)

У вас есть бэкенд, который отдает профиль пользователя. Отдает щедро:

{
    "id": 123,
    "name": "Вася",
    "email": "vasya@mail.ru",
    "createdAt": "2023-01-01",
    "updatedAt": "2023-12-31",
    "address": "...",
    "phone": "..."
}

А на фронте для карточки пользователя нужно только name и email. Остальные данные в контексте этой задачи — мусор, который только увеличивает количество трафика, которое гуляет по сети.

Применяя BFF, мы получим такую схему: бэкенд отдает всё на BFF, а BFF отдает фронту только нужное.

{
    "name": "Вася",
    "email": "vasya@mail.ru"
}

Проблема №2: Андерфетчинг (нехватка данных)

У вас страница профиля, на которой нужно показать:

  • Данные пользователя (из сервиса user-service)
  • Аватар (из сервиса file-service)
  • Количество подписчиков (из сервиса social-service)

Без BFF фронт должен сделать 3 последовательных запроса:

  1. GET /users/123 → получаем пользователя.
  2. GET /file/123/avatar → получаем аватар.
  3. GET /social/followers/123 → получаем подписчиков.

Каждый запрос — это задержка (даже если 100 мс, 3 запроса = 300 мс). И это, допустим, на быстрой сети. А если запрос делается через дохленький 3G?

С BFF фронт сделает всего 1 запрос на BFF, например: GET /api/profile/123. BFF параллельно (или последовательно, если нужно) сходит в 3 сервиса, соберет данные, и отдаст все сразу.

{
    "user": { "name": "Вася", "email": "vasya@mail.ru" },
    "avatar": "https://cdn.site/avatar123.jpg",
    "followersCount": 42
}

Один запрос вместо трех. Страница грузится быстрее.

Проблема №3: Разные требования у разных клиентов

  • Веб-версия: нужны картинки, длинные тексты, много декоративных данных, медленный интернет не проблема.
  • Мобильное приложение: нужны только ключевые данные, картинки маленькие, важна скорость и экономия трафика.
  • Админка: нужны все данные, включая служебные (статусы, метаданные).
  • Публичное API (для сторонних разработчиков): нужна стабильная структура, которая не меняется годами.

Бэкенд не может держать в голове требования всех клиентов. Он просто отдает «сырые» данные. А BFF можно сделать для каждого клиента свой:

  • bff-web.yourapp.com — для веба.
  • bff-mobile.yourapp.com — для мобилки.
  • bff-admin.yourapp.com — для админки.

Каждый BFF адаптирует данные под свой тип клиента.

Проблема №4: Сложность клиентского кода

Без BFF фронтенд превращается в спагетти-код из запросов:

// Без BFF — боль и страдания
const user = await fetch('/users/123');
const avatar = await fetch('/media/123/avatar');
const followers = await fetch('/social/followers/123');
const posts = await fetch('/posts/123');
// ... и так далее

С BFF код фронта куда как более чистый:

// С BFF — красота
const profile = await fetch('/api/profile/123');

Один запрос вместо четырёх. Вся логика агрегации ушла на сервер.

А правда ли всем нужен BFF?

BFF нужен, если:

  • У вас на бэке микросервисная архитектура (много отдельных бэкендов)
  • У вас несколько типов клиентов (веб, мобилка, админка, API для партнеров)
  • Фронт делает много последовательных запросов для отображения оодной страницы
  • Бэкенд отдает слишком много лишних данных
  • Вы хотите, изолировать внутреннюю структуру бэкенда от внимания клиентов (так безопаснее).

BFF — скорее всего оверхед, если:

  • У вас на бэке монолит
  • У вас один тип клиента (например, только веб)
  • Приложение простое и данных мало

Недостатки BFF

  1. BFF — это дополнительный сервис — егонужно разворачивать, поддерживать, мониторить. Это расходы
  2. Увеличение задержки — запрос сначала идет на BFF, потом на бэкенд, потом обратно. Поэтому BFF должен быть быстрым, иначе профита не будет
  3. Дублирование логики — если у вас несколько BFF (для веба, мобилки и админки), код частично дублируется. Решение — писать общие библиотеки, но это дополнительные издержки
  4. Сложность с кэшированием — агрегированные данные сложнее кэшировать, потому что они зависят от многих источников.
  5. Единая точка отказа — если BFF упал, все клиенты падают. Здесь нужно позаботиться о высокой доступности

Альтернативы BFF

  1. GraphQL

Вместо того чтобы писать BFF вручную, вы используете GraphQL Gateway (Apollo Federation). Клиент сам решает, какие данные ему нужны (через запросы), а шлюз собирает их с разных сервисов.

Плюсы: Гибкость (клиент сам выбирает поля), меньше кода для BFF.
Минусы: Сложность настройки, клиенты становятся «умными».

  1. Сервисный слой на фронте (Service Layer)

Вы не пишете отдельный сервер, а делаете на фронте слой, который выполняет задачи BFF — ходит в разные API и агрегирует данные.

Плюсы: Нет дополнительного сервиса.
Минусы: Все запросы идут с клиента, что медленнее и небезопасно (ключи доступа видны)

  1. API Gateway (более низкий уровень)

Это не BFF, а общий шлюз для всех запросов (маршрутизация, авторизация, лимиты). Он не агрегирует данные, просто перенаправляет запросы.

Плюсы: Все запросы проходят через единую точку, где можно централизованно управлять аутентификацией, лимитами, логированием и маршрутизацией, не трогая каждый микросервис по отдельности
Минусы: Если шлюз упал — упало всё приложение, плюс каждый запрос получает дополнительную задержку (5-20 мс)

Итого

BFF — это не панацея. Это инструмент для сложных проектов. Маленьким приложениям он не нужен, но когда проект разрастается, BFF становится спасением, которое избавляет фронтенд-команду от боли работы с множеством запросов и кучами лишних данных в рамках клиентского кода.

Желаю успехов!

Симо Мофин
Симо Мофин

Senior Frontend Developer
Главный по блогу