08 мая 2025 г.·5 мин чтения

Слой backend-for-frontend для веб‑ и мобильных приложений

Слой backend-for-frontend помогает веб‑ и мобильным приложениям получать нужные данные, сохраняя бизнес‑правила в одном общем бэкенде.

Слой backend-for-frontend для веб‑ и мобильных приложений

Почему один API становится проблемным

Один API кажется аккуратным, когда продукт маленький. Один контракт проще объяснить, протестировать и изменить. Потом продукт растёт.

Веб‑приложение начинает запрашивать целые таблицы, фильтры, итоги и дополнительную метаинформацию. Мобильное приложение хочет меньшие полезные нагрузки, меньше запросов и экраны, которые быстро загружаются при нестабильном соединении. Оба клиента инициируют одни и те же бизнес‑действия, но им не нужен один и тот же ответ.

Эта разница быстро создаёт трение. Универсальный эндпойнт шлёт одному клиенту слишком много данных и всё ещё оставляет дыры для другого. Веб‑команда получает поля, которые никогда не рендерятся. Мобильной команде нужен ещё один флаг статуса, одна вычисляемая метка или более короткий список для маленького экрана, поэтому она делает дополнительные вызовы и собирает результаты в приложении.

Когда это начинается, фронтенд наполняется правилами, которые туда не относятся. Один клиент решает, когда заказ просрочен. Другой использует слегка другой порог. Скидка видна в вебе, но не в мобильном, потому что каждое приложение по‑своему воссоздало одну и ту же проверку. Команды так делают, потому что нужно выпустить релиз, а не потому что это хорошая архитектура.

Через несколько релизов продукт расползается. Веб сортирует элементы одним образом. Мобильный группирует по‑другому. Ошибка исправлена в iOS, но не на вебе. Поддержка видит разные ответы для одного и того же аккаунта, а отладка превращается в охоту за следами.

Дашборд делает проблему очевидной. В вебе пользователю могут быть нужны итоги, тренды, фильтры и полный список активности. На мобильном тот же человек может увидеть три оповещения, суммарное значение за сегодня и один понятный следующий шаг. Если один бэкенд пытается удовлетворить оба требования одним ответом, он обычно «высаживается посередине» и плохо служит обоим.

Именно в этот момент имеет смысл слой backend‑for‑frontend.

Что делает слой backend‑for‑frontend

Слой backend‑for‑frontend (BFF) располагается между клиентом и вашим основным бэкендом. Его задача проста: сформировать данные под клиента, который их запрашивает.

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

Он также может объединять несколько вызовов бэкенда в один ответ. Вместо того чтобы приложение по очереди запрашивало данные пользователя, статус аккаунта, недавнюю активность и флаги функций, BFF может собрать их и вернуть один аккуратный объект. Это удаляет «клей» из клиента и часто делает экраны быстрее.

Что остаётся в основном бэкенде

Здесь команды часто спотыкаются. BFF должен формировать данные для представления. Он не должен становиться местом, где живут бизнес‑правила.

Цены, права доступа, правила одобрения, логика скидок и состояние рабочих процессов должны оставаться в основном бэкенде, где у каждого клиента одна и та же истина. BFF может переименовывать поля, обрезать неиспользуемые данные, сливать ответы или подгонять пагинацию под мобильный экран. Он не должен решать, кому вернуть деньги или может ли пользователь одобрить платёж.

Представьте продукт с веб‑админкой и мобильным приложением для клиентов. Оба зависят от одних и тех же правил заказов. Дашборду нужны итоги, фильтры, данные аудита и действия сотрудников. Мобильному приложению нужен статус заказа, время доставки и следующий шаг для клиента. BFF может вернуть оба вида представления аккуратно, в то время как общий бэкенд сохраняет реальные правила заказа в одном месте.

Такое разделение стоит охранять. Фронтенд‑команды двигаются быстрее, а поведение бэкенда остаётся согласованным.

Когда этот паттерн уместен

BFF полезен, когда вы выпускаете один и тот же продукт на вебе и мобильных, но каждый клиент требует данных в иной форме. Бизнес‑правила остаются общими. Размер полезной нагрузки, поток экранов и шаблоны запросов могут отличаться.

Чаще всего проблему первым обнаруживает мобайл. Десктопное приложение переживёт более тяжёлый ответ и несколько дополнительных вызовов. На телефоне это оплатится более медленной загрузкой экранов, большим количеством состояний загрузки и расходом батареи. Если мобильная команда постоянно просит меньшие полезные нагрузки, а веб‑команда — более богатые ответы, один общий API часто становится неуклюжим.

Этот паттерн также помогает, когда фронтенд часто меняется. Экраны переставляют. Шаг оформления заказа объединяют. Карточке дашборда внезапно нужны данные из трёх сервисов. Если каждое небольшое изменение UI должно ждать основного бэкенда, бэкенд превращается в бутылочное горлышко.

Самый очевидный признак прост: исправление одного клиента делает другого хуже. Вы добавляете поля для веба, и мобильный замедляется. Вы упрощаете ответ для мобайла, и вебу приходится делать дополнительные вызовы. Обычно это признак того, что один API пытается решать две очень разные задачи.

Это подходит, когда:

  • веб и мобильный показывают один и тот же бизнес‑процесс по‑разному
  • производительность мобильного достаточна критична, и размер полезной нагрузки реально мешает
  • релизы фронтенда происходят чаще, чем релизы бэкенда
  • вы хотите менять логику экрана, не вынося доменные правила из общего бэкенда

Страница аккаунта — распространённый пример. Веб‑версия может загружать профиль, недавнюю активность, счета и админ‑опции в одном насыщенном представлении. Мобильная версия может требовать только профиль и несколько последних действий в одном компактном ответе. Если продукт постоянно порождает такие различия, вероятно, стоит попробовать BFF.

Как это настроить

Начните с экранов, а не с эндпойнтов. Поставьте рядом веб‑ и мобильные потоки и запишите, что нужно каждому экрану при первой загрузке, после действия пользователя и при ошибке. Это обычно быстро выявляет реальные разрывы.

Затем отделите логику представления от бизнес‑правил. Логика представления включает имена полей, группировку, порядок сортировки, размеры изображений и нужно ли экрану резюме или полные детали. Бизнес‑правила покрывают права доступа, ценообразование, проверки запасов, валидацию и изменения статуса. Держите эти правила в общих сервисах до того, как добавите клиент‑специфичные эндпойнты.

Практичная настройка обычно выглядит так:

  1. Напишите небольшой контракт данных для каждого экрана, вместо одного гигантского контракта для всего приложения.
  2. Перенесите общие бизнес‑решения в бэкенд‑сервисы до того, как строить клиент‑специфичные ответы.
  3. Добавляйте тонкие BFF‑эндпойнты только там, где веб и мобильный действительно нуждаются в разных формах ответа.
  4. Повторно используйте одинаковые подходы к аутентификации, логированию и кэшированию по всему пути запроса.
  5. Измеряйте размер полезной нагрузки, задержки и пути ошибок с самого начала.

Тонкость — вот что важно. BFF должен формировать данные, объединять несколько вызовов и убирать «клей» из клиента. Если он начинает владеть правилами, рабочими процессами или политиками, вы случайно создали второй бэкенд.

Следите за метриками с раннего этапа. Если мобильная полезная нагрузка упала с 180 КБ до 45 КБ, пользователи это заметят. Если один экран по‑прежнему занимает 1.8 секунды, потому что BFF вызывает четыре сервиса последовательно, исправьте это до запуска. Дополнительный слой должен упростить клиентов и облегчить трассировку ошибок.

Простой пример для веба и мобайла

Сопоставьте контракты экранов
Определите, что нужно вебу и мобильным до того, как ваш API вырастет вокруг догадок.

Онлайн‑магазин хорошо иллюстрирует, потому что привычки покупок в вебе и на мобайле разные.

На сайте люди дольше просматривают товары, сравнивают, читают детали, добавляют промо‑коды и проходят полный чек‑аут. На мобильном обычно хотят скорости: ищут, нажимают, повторно заказывают и уходят.

Это создаёт нагрузку для одного общего API. Веб просит фильтры товаров, данные для сравнения, промо‑блоки, сохранённые корзины и полные детали оформления. Мобайл не хочет большую часть из этого. Ему нужен короткий ответ, быстрые результаты поиска, недавние заказы и упрощённый поток оформления.

BFF решает это, не деля бизнес‑логику на две части. Веб‑сторона может подготовить ответ с фильтрами категорий, бейджами товаров, полями сравнения и деталями оформления. Мобильная сторона может вернуть гораздо меньший ответ с подсказками поиска, кнопкой «повторить заказ» и минимальными данными, необходимыми для покупки на маленьком экране.

Оба клиента по‑прежнему опираются на один и тот же сервис заказов внизу. Этот сервис сохраняет правила, которые влияют на деньги и состояние: расчёт налогов, проверки запасов, правила оплаты, валидацию скидок и смену статуса заказа.

Представьте, кто‑то покупает капсулы для кофе. На сайте он сравнивает размеры упаковок, читает условия доставки, добавляет промо‑код и проверяет подробное резюме перед оплатой. На мобильном он открывает приложение в поездке, нажимает "buy again", подтверждает сохранённый адрес и оплачивает за секунды. Правила те же. Меняется только форма ответа и поток экрана.

Вот в чём смысл. Веб‑разработчикам не нужно таскать мобильные ярлыки, которые они никогда не используют, а мобильным разработчикам не нужны тяжёлые данные веб‑чекаута ради одной кнопки «повторить заказ».

Где провести границу

BFF должен формировать данные, а не изобретать бизнес‑правду. Если он начинает решать цены, наличие на складе или права аккаунта, у вас больше нет единой системы записи. Появляются две версии одной и той же логики продукта, и они со временем разойдутся.

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

Менять правила, лежащие в основе заказа, — нельзя.

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

Эта граница также упрощает добавление будущих клиентов. Если позже появится портал партнёра, приложение для планшета или новый поток оформления, каждый сможет получить ответ, подходящий под его макет, в то время как реальное поведение бизнеса остаётся общим.

Типичные ошибки команд

Держите правила в одном месте
Верните ценообразование, права и изменения состояния в единый источник правды.

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

Ещё одна проблема — копирование эндпойнтов. Команды создают /orders-web и /orders-mobile, клонируют туда почти одинаковый код и обещают почистить позже. Позже редко наступает. Каждое изменение поля превращается в дополнительные правки, тесты и шанс что‑то пропустить.

Операции тоже становятся сложнее. Одна команда владеет основным API, другая — BFF, и никто не отвечает за полный путь запроса. Задержки растут на каждом этапе. Логи живут в разных местах. Сообщения об ошибках меняют форму между слоями. Когда пользователь получает таймаут, каждая команда говорит, что её сервис в порядке.

Несколько ранних признаков:

  • BFF хранит правила, о которых основной бэкенд не знает
  • два эндпойнта отличаются лишь несколькими полями
  • команды спорят, какой слой должен возвращать ошибку
  • веб и мобильный видят разные ответы для одного аккаунта

Назначьте одного владельца для бюджетов задержек, трассировки и формата ошибок по всей цепочке. Это звучит скучно, но экономит много боли позже.

Проверки перед запуском

Трассируйте полный путь запроса
Поможем с логами, задержками и потоком ошибок между клиентом, BFF и бэкендом.

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

Начните с самого ответа. Откройте реальные экраны на вебе и в мобильном, затем посмотрите полезные нагрузки. Каждый клиент должен получать только те поля, которые он рендерит или которые нужны для следующего действия. Если мобильное приложение всё ещё тянет данные только для десктопа, вашей форме ответа ещё далеко до оптимальной.

Затем проверьте простое изменение правила. Поменяйте что‑то тривиальное, например, кто видит скидку или когда кнопка должна оставаться заблокированной. Обновите правило один раз и подтвердите, что оба клиента следуют ему без отдельных правок. Если один экран всё ещё требует кастомной логики во фронтенде, ваши общие бизнес‑правила на самом деле не общие.

Трассировка — другой сложный чек. При падении запроса команда должна проследить его от клиента до BFF и далее до основного бэкенда без догадок. Идентификаторы запросов должны совпадать. Логи должны рассказывать одну историю. Сообщения об ошибках должны указывать на правильный слой.

До запуска убедитесь, что:

  • веб и мобильный могут менять компоновку без переноса бизнес‑логики в клиент
  • команды знают, какие поля ещё используются каждым клиентом и какие можно удалить
  • одна сломанная зависимость быстро заметна в логах и мониторинге
  • фронтенд может запросить новую форму экрана без изменения доменных правил

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

Что делать дальше

Выберите один поток, который уже болит: медленный чек‑аут, хрупкий экран аккаунта или мобильная страница с кучей API‑вызовов. Начните с него. BFF должен решать реальную проблему, а не быть побочным проектом.

Сделайте первую версию узкой. Добавляйте её только туда, где веб и мобильный действительно отличаются по размеру полезной нагрузки, группировке данных или таймингу запросов. Если оба клиента могут использовать один ответ с небольшими изменениями UI, оставьте эндпойнт в общем API и двигайтесь дальше.

Запишите границу до того, как кто‑то начнёт писать код. Цены, права и правила рабочего процесса остаются в общем бэкенде. Выбор полей, форматирование ответа и объединение вызовов ради одного экрана может жить в BFF. Любое правило, которое должно совпадать во всех клиентах, требует одного владельца.

Затем измерьте результат. Проверьте время ответа, размер полезной нагрузки, количество кода, который удалось убрать в клиентах, и число багов для этого потока. Если числа почти не изменились, остановитесь. Во многих случаях приведение в порядок одного грязного эндпойнта даёт больше пользы, чем внедрение паттерна BFF везде.

Если компромисс всё ещё неочевиден, короткий архитектурный обзор спасёт вас от владения неправильным слоем на следующие два года. Oleg Sotnikov at oleg.is работает со стартапами и небольшими командами по вопросам архитектуры продукта, границ бэкенда и инфраструктуры, так что это тот случай, с которым он регулярно имеет дело. Иногда ответ — BFF. Иногда — один чище API и меньше кода для поддержки.

Часто задаваемые вопросы

Что такое слой backend-for-frontend?

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

Когда веб и мобильный нужны отдельные ответы?

Когда веб и мобильное приложение показывают один и тот же бизнес‑процесс по‑разному и общий ответ наносит вред обеим сторонам. Если мобильный требует лёгких полезных нагрузок, а веб — больше деталей, BFF часто решает этот разрыв.

Означает ли BFF, что мне нужны два бэкенда?

Нет. Оставьте один основной бэкенд для ценообразования, прав доступа, учёта запасов и изменений статусов. BFF должен только формировать поля, комбинировать запросы и обрезать данные под каждый клиент.

Какую логику следует держать вне BFF?

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

Ускорит ли BFF моё приложение?

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

Как начать, не создав ещё более крупный беспорядок?

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

Какой самый ясный признак, что один API больше не подходит?

Простой признак — исправление одного клиента ухудшает другого. Если веб просит больше полей и мобильный тормозит, или вы упрощаете ответ для мобильного, а веб вынужден делать дополнительные вызовы, один API больше не подходит для обеих задач.

Стоит ли строить BFF для каждого эндпойнта?

Нет. Стройте BFF только для тех экранов, где клиенты действительно нуждаются в другой полезной нагрузке, группировке данных или тайминге запросов. Если оба клиента могут использовать один ответ с небольшими изменениями UI, оставьте эндпойнт в общем API.

Как проверить границу перед запуском?

Тестируйте реальные экраны, а не только документацию. Проверьте, что каждый клиент получает только поля, которые он рендерит, измените одно общее правило и убедитесь, что оба клиента следуют ему, и пройдите трассировку сбоя от клиента до BFF и далее по одному request‑ID.

Когда следует пропустить паттерн BFF?

Пропустите его, когда клиенты получают почти одинаковые данные и реальная проблема лежит в другом месте. Если один более чистый API решает проблему, это обычно даёт меньше кода, меньше точек отказа и меньше дрейфа со временем.