Backend-for-Frontend (BFF)¶
Backend-for-Frontend - отдельный server-side слой под один конкретный фронтенд. Он собирает данные downstream-сервисов и подгоняет их под нужды именно этого UI.
Note
BFF путают с API Gateway: оба стоят на входе перед сервисами. Gateway - один общий вход на всех клиентов, BFF - по одному на каждый тип клиента (см. BFF и API Gateway).
Паттерн родом из SoundCloud. Практику описал Phil Calçado в 2015 году (philcalcado.com), а сам термин BFF, по его словам, придумал Nick Fisher, tech lead веба в SoundCloud. Как общий паттерн BFF описал Sam Newman (ThoughtWorks, ноябрь 2015) и разнёс по индустрии в статьях и книгах про микросервисы.
Зачем BFF¶
Один backend на всех клиентов - вечный компромисс:
- Мобайл хочет мало: узкий канал, дорогой трафик, маленький экран. Ему нужно три поля, а общий endpoint отдаёт двадцать.
- Web хочет много: больше данных за раз, другие экраны, другая навигация.
- Under-fetching и over-fetching одновременно: один клиент недополучает, другой тащит лишнее.
- Клиенты развиваются с разной скоростью, и общий API становится местом, где сталкиваются все команды.
Что из этого выходит: каждая UI-команда правит общий контракт под себя, и API превращается в кашу из query-параметров и условий, где никому не удобно. Любое изменение под мобайл рискует задеть web и наоборот. Координация дорожает с числом клиентов.
Решение: дать каждому клиенту свой backend. Мобильная команда оптимизирует свой endpoint под мобайл, web-команда - под web, и никто никому не мешает.
Когда подходит:
- несколько клиентов с реально разными нуждами: мобайл, web, партнёрский API
- у каждого клиента своя команда, и их релизы хочется развязать
- общий API уже стал узким местом, где дерутся за поля
Когда BFF не нужен¶
- Один клиент, один web. Тогда BFF - это просто твой backend, отдельное имя ему не нужно.
- Клиенты почти одинаковые. Дублирование съест выгоду, проще жить с общим API.
Как устроено¶
iOS app Android app Web SPA
| | |
+----------+ +----------+ +----------+
| iOS BFF | | Android | | Web BFF |
| (thin) | | BFF | | (thin) |
+----------+ +----------+ +----------+
| \ / \ / |
| \ / \ / |
v v v v v v
+---------+ +-----------+ +-----------+ +-----------+
| Orders | | Catalog | | Users | | Pricing | downstream services
+---------+ +-----------+ +-----------+ +-----------+
- Один UI - один BFF: iOS-BFF, Android-BFF, web-BFF. Клиент ходит только в свой BFF.
- BFF делает fan-out по нужным сервисам, склеивает ответы и отдаёт ровно ту форму, что нужна экрану. По сути это API Composition под один UI.
- Downstream-сервисы общие. BFF их не дублирует, а композирует.
- BFF тонкий: собирает ответы, переводит протоколы, режет лишние поля. Domain logic в нём не живёт.
Граница владения тут организационная, а не техническая - это закон Конвея. BFF принадлежит той же команде, что пишет клиент. Контракт между клиентом и BFF меняется внутри одной команды, без межкомандной координации.
Плюсы и минусы¶
Плюсы:
- UI-команда владеет своим backend целиком. Меняешь экран - меняешь свой BFF, никого не спрашивая и не дерясь за общий gateway.
- Каждый клиент получает ровно свою форму данных, без over-fetching и under-fetching.
- BFF деплоится независимо от других BFF. Релизный цикл клиента не завязан на чужие.
- Downstream-сервисы остаются чистыми и generic: вся клиент-специфика уезжает в BFF.
Минусы:
- Дублирование. Похожая агрегация и трансляция переписываются в каждом BFF: web и Android часто хотят почти одно и то же и пишут это дважды.
- N клиентов - N backend-ов, которые надо писать, деплоить, мониторить и дежурить.
- Общий код между BFF. Вынесешь в shared-библиотеку - снова свяжешь команды, не вынесешь - копипаста. Ещё вариант - опустить общую логику в нижележащий сервис. Золотой середины нет, выбираешь по контексту.
Грабли:
- BFF - не место для бизнес-логики. Это тонкий слой сборки и перевода. Как только в нём появляются решения «можно ли», а не «как показать», это уже не BFF, а скрытый сервис. Бизнес-правила остаются в сервисах, иначе BFF расползается в god gateway, только теперь их несколько.
- Один общий BFF на всех - это снова API Gateway под другим именем, и вся затея теряет смысл.
BFF и API Gateway¶
Оба сидят на входе, оба стоят перед сервисами. Разница в количестве и в назначении.
| Свойство | API Gateway | BFF |
|---|---|---|
| Сколько | один общий вход на всех | по одному на каждый UX |
| Кому служит | всем клиентам сразу | конкретному клиенту |
| Что делает | routing, auth, rate limit, единый вход | агрегация и подгонка под один UI |
| Кто владеет | платформенная или infra-команда | UI-команда этого клиента |
| Форма ответа | более-менее generic | заточена под экраны клиента |
- Одно другому не мешает. Часто BFF стоят за общим gateway: gateway держит auth и rate limit на периметре, дальше трафик расходится по BFF. Gateway - горизонталь, BFF - вертикали под ним.
- Richardson подаёт BFF как вариант gateway: отдельный gateway на каждый тип клиента. Акцент другой: BFF не стесняется логики «под экран», gateway держит общие сквозные задачи.
Итого: gateway - общая дверь для всех, BFF - по двери на клиента, и каждая знает свой UI в лицо.