BFF расшифровывается как Backend for Frontend — это архитектурный паттерн, при котором для каждого типа клиентского приложения (веб, мобильное, Smart TV) создаётся отдельный серверный прослойка-сервис, адаптирующий данные именно под нужды этого клиента. Если в вашем проекте мобильное приложение вынуждено делать пять запросов к разным микросервисам, чтобы отрисовать один экран, а веб-версия получает в ответе килобайты лишних полей — это типичный симптом отсутствия BFF-слоя.
Паттерн приобрёл популярность вместе с распространением микросервисной архитектуры: чем больше в системе независимых сервисов, тем сложнее клиенту собирать из их ответов цельную картину. BFF решает эту задачу, забирая логику агрегации и трансформации данных на серверную сторону, ближе к источникам данных.
Как устроен паттерн Backend for Frontend
Классическая схема выглядит так: клиент обращается не к десятку микросервисов напрямую, а к своему выделенному BFF-сервису. Тот, в свою очередь, запрашивает нужные внутренние сервисы, объединяет ответы, отбрасывает лишнее и возвращает клиенту один компактный результат в удобном формате.
Ключевая идея — один BFF на один тип клиента. Веб-приложение и мобильное приложение имеют разные требования: мобильному клиенту важны минимальный трафик и малое число запросов (экономия батареи и работа на нестабильной сети), веб-клиенту — богатые данные для сложных интерфейсов. Общий универсальный API неизбежно становится компромиссом, который плохо обслуживает обе стороны.
- 📱 Mobile BFF — отдаёт урезанные модели данных, агрегирует несколько вызовов в один, учитывает ограничения мобильных сетей.
- 🖥️ Web BFF — формирует данные для SSR-рендеринга, управляет сессиями и cookies, кэширует тяжёлые ответы.
- 📺 BFF для других клиентов — Smart TV, голосовые ассистенты, партнёрские интеграции со своими форматами.
BFF — это не замена микросервисам, а тонкий адаптер между ними и конкретным клиентом: он не содержит бизнес-логики, а только агрегирует и форматирует данные.
Какие проблемы решает BFF
Без выделенного слоя клиентские команды вынуждены знать внутреннее устройство бэкенда: адреса сервисов, форматы их ответов, порядок вызовов. Любое изменение в микросервисах ломает клиентов, а релизы приходится синхронизировать.
BFF снимает эту связанность. Клиент зависит только от контракта своего BFF, а изменения внутренних сервисов поглощаются на уровне адаптера. Дополнительно решаются задачи, которые неудобно или небезопасно делать на клиенте:
- 🔐 Сокрытие токенов и внутренних адресов сервисов от клиентского кода.
- ✂️ Удаление лишних полей из ответов — клиент получает только то, что реально отображает.
- 🔗 Объединение нескольких внутренних вызовов в один внешний запрос.
- 🔄 Преобразование протоколов — например, клиент работает по REST/JSON, а внутренние сервисы общаются через gRPC.
⚠️ Внимание: BFF не должен превращаться в место, где «живёт» бизнес-логика. Если в BFF появляются расчёты скидок, проверки прав или сложные правила — они размножатся по всем BFF и начнут расходиться. Такую логику следует держать в доменных сервисах.
BFF против API Gateway: в чём разница
Эти два подхода часто путают, потому что оба стоят «между клиентом и сервисами». Разница в назначении: API Gateway — единая точка входа с инфраструктурными задачами (аутентификация, rate limiting, маршрутизация), тогда как BFF — клиенто-специфичный адаптер данных.
| Критерий | API Gateway | BFF |
|---|---|---|
| Количество экземпляров | Один на всю систему | Отдельный для каждого типа клиента |
| Основная задача | Маршрутизация, безопасность, лимиты | Агрегация и адаптация данных под клиента |
| Кто разрабатывает | Инфраструктурная команда | Команда конкретного клиента |
| Знание о клиенте | Минимальное | Глубокое, формат под конкретный UI |
На практике подходы комбинируются: запрос проходит через API Gateway (проверка токена, лимиты), затем попадает в соответствующий BFF, который собирает данные из внутренних сервисов. Это нормальная и распространённая схема, а не избыточность.
Когда BFF действительно нужен, а когда — лишний
Не каждому проекту требуется этот слой. Внедрение BFF оправдано, когда выполняется хотя бы пара условий: несколько разнородных клиентов, микросервисная архитектура, независимые команды разработки клиентов, высокие требования к оптимизации трафика.
Если у вас один веб-клиент и простой бэкенд из двух-трёх сервисов, отдельный BFF добавит лишь дополнительный сервис для поддержки и развёртывания. В таком случае проще расширить существующий бэкенд. Признак, что BFF пора вводить: клиентская команда тратит заметную часть времени на «склейку» ответов разных API и обход их несогласованности.
☑️ Стоит ли внедрять BFF — проверьте себя
Технологии и типичная реализация
BFF обычно пишут на том же стеке, что и клиент, чтобы команда могла развивать его самостоятельно: для веба это часто Node.js, для мобильных клиентов — Kotlin, Swift или тот же Node.js. Популярный вариант — GraphQL-сервер в роли BFF: клиент сам описывает, какие поля ему нужны, а сервер собирает их из внутренних источников.
Упрощённый пример агрегирующего обработчика на Node.js:
app.get('/api/profile-summary', async (req, res) => {
const [user, orders, balance] = await Promise.all([
userService.get(req.userId),
orderService.list(req.userId),
billingService.balance(req.userId)
]);
res.json({
name: user.name,
ordersCount: orders.length,
balance: balance.amount
});
});
Обратите внимание: обработчик не содержит бизнес-правил — он только параллельно запрашивает три сервиса и формирует компактный ответ. Именно такой и должна быть роль BFF.
Запускайте независимые внутренние запросы параллельно (Promise.all, async/await с gather-семантикой), а не последовательно — иначе BFF добавит клиенту лишнюю задержку вместо выигрыша.
Типичные ошибки при внедрении
Первая ошибка — создание одного общего BFF на всех клиентов. Он быстро превращается в тот же универсальный API, от которого уходили, только с дополнительным сетевым хопом. Вторая — дублирование логики между BFF: если одинаковый код агрегации нужен в нескольких BFF, его стоит вынести в общий внутренний сервис.
⚠️ Внимание: каждый дополнительный сетевой слой — это точка отказа и задержка. BFF нужно мониторить так же, как основные сервисы: время ответа, процент ошибок, таймауты вызовов внутренних API. Без наблюдаемости диагностика проблем «у клиента не грузится экран» станет заметно сложнее.
Откуда пошёл термин BFF
Концепцию описал и популяризировал Сэм Ньюмен — автор книги «Building Microservices» — на примере компании SoundCloud, где для мобильного приложения создали отдельный бэкенд-слой, отличный от API для веба. Аббревиатура совпадает с разговорным «best friends forever», но в инженерном контексте означает именно Backend for Frontend.
FAQ: частые вопросы о BFF
BFF и прокси-сервер — это одно и то же?
Нет. Прокси просто пересылает запросы, не меняя их содержимое. BFF активно обрабатывает данные: объединяет ответы нескольких сервисов, фильтрует поля, преобразует форматы под конкретного клиента.
Можно ли использовать GraphQL вместо BFF?
GraphQL-сервер часто играет роль BFF, поскольку позволяет клиенту запрашивать только нужные поля. Но если клиенты сильно различаются по логике (например, разные схемы авторизации или сценарии), отдельные BFF всё равно могут понадобиться — иногда это несколько GraphQL-шлюзов с разными схемами.
Кто должен разрабатывать и поддерживать BFF?
Рекомендуемый подход — команда того клиента, которому BFF служит: веб-команда владеет Web BFF, мобильная — Mobile BFF. Так сокращаются кросс-командные зависимости и ускоряются релизы, что является одной из главных целей паттерна.
Сколько BFF нужно на проект?
Ориентир — один BFF на один тип клиентского опыта, а не на каждое устройство. Веб и мобильное приложение почти всегда требуют разных BFF, а iOS и Android обычно могут пользоваться общим мобильным BFF, если их потребности в данных совпадают.
Подходит ли BFF для монолитного бэкенда?
Технически да, но выгода меньше: BFF раскрывается именно при множестве внутренних сервисов, чьи ответы нужно агрегировать. С монолитом проще добавить клиенто-специфичные эндпоинты прямо в него, не вводя отдельный сервис.
BFF оправдан при микросервисах и нескольких разнородных клиентах: он убирает агрегацию с клиента, снижает связанность команд и оптимизирует трафик. Для простого проекта с одним клиентом паттерн будет избыточным.