Представим себе такую ситуацию, что backend часть проекта разбросана по разным микросервисам, например, нам необходимо загрузить страницу, на которой отображается информация пользователя с его уведомлениями, а также таблица с необходимой информацией, все эти данные разбросаны по разным микросервисам, то есть, чтобы получить данную информацию нам необходимо "стучаться" по разным URL, при этом следить чтобы запросы отправлялись в правильной последовательности.
Данный процесс довольно трудоемкий, но что если есть способ решения данной проблемы?
Именно здесь появляется BFF (Backend For Frontend) - это архитектурный паттерн, основная идея которого - это создание отдельного серверного слоя, который представляет из себя прокси сервер, то есть он занимается отправкой запроса на нужный сервис и преобразует ответ в вид, который необходим фронтенду.
Самой наилучшей аналогией, которая представляет BFF является официант. Когда мы приходим в ресторан, то вместо того, чтобы бегать к каждому повару отдельно за необходимыми заказами(делать запросы к разным сервисам), мы работаем с официантом(BFF), мы предоставляем ему информацию о том, какие блюда нам нужны(один запрос к BFF), а официант сам передаст информацию необходимым поварам, соберет все заказы и принес нам за стол.
В данной статье мы разберем BFF и его тонкости.
Что такое BFF? Основные концепции
Мы уже сделали вывод, что BFF - это персональный backend для нашего frontend.
Основная идея данного паттерна заключается в том, что каждый клиент, будь то веб-приложение, мобильное приложение и тд имеет свой "сервер-компаньон", который:
1) Знает его специфические требования (например, нам необходимо отправлять на мобильное приложение ответ с меньшим кол-вом полей, чем на веб-приложение)
2) Оптимизирует работу с серверной частью приложения
3) Предоставляет специализированный API для конкретного UI
При этом важно понимать, что BFF - это архитектурный паттерн, который реализовывает связь между клиентом и сервером, а не фреймворк или библиотека.
Ключевый характеристики BFF:
Индивидуальность
BFF создается для конкретного клиента и "заточен" на:
1) Особенности отображения данных на клиенте.
2) Особенности сетевого взаимодействия.
3) Специфические требования к формату данных.
4) Оптимальные стратегии кэширования данных."Тонкий" сетевой слой
BFF не должно содержать бизнес логики, является крайне важно. Основные функции BFF:
1) Агрегация данных - получение необходимых данных из разных сервисов в единую модель.
2) Трансформация данных под клиентские нужны.
3) Маршрутизация запросов к необходимым сервисам.
4) Обработка ошибок.
Использование данного паттерна в настоящее время становится частой практикой среди frontend сообщества, это связано стем, что:
1) Микросервисная архитектура все чаще и чаще используется на проектах.
2) Большое разнообразие клиентских устройств.
Архитектура BFF в деталях
Рассмотрим общую схему работы BFF:
Как запросы проходят через BFF:
1) Запрос с клиента - frontend отправляет один запрос к BFF.
2) Данные попадают в прослойку и начинается работа BFF архитектуры.
3) Аутентификация - проверка токенов и параметров.
4) Параллельные запросы - обращение к микросервисам, которые необходимы.
5) Агрегация данных - объединение результатов по запросам к микросервисам.
6) Трансформация данных - преобразование данных в необходимый для клиента вид.
7) Ответ на клиент - передача сформированного ответа на сторону frontend.
BFF состоит из нескольких логических компонентов:
- Маршрутизатор, который определяет, какой обработчик должен выполнить запрос и провалидировать данные.
// Пример маршрутизации с Express.js
app.get('/web/products/:id', productPageHandler);
app.get('/web/user-profile', userProfileHandler);
- Агрегатор данных (Data aggregator), который управляет параллельными запросами и объединяет их результаты.
class DataAggregator {
async aggregateProductPageData(productId) {
const [product, reviews, recommendations, stock] = await Promise.all([
this.catalogService.getProduct(productId),
this.reviewService.getReviews(productId),
this.recommendationService.getSimilar(productId),
this.inventoryService.getStockInfo(productId)
]);
return { product, reviews, recommendations, stock };
}
}
- Трансформеры (Transformers), которые преобразуют данные в формат необходимый клиенту.
class ProductTransformer {
static transformForWeb(productData) {
const { product, reviews, recommendations, stock } = productData;
return {
productInfo: {
id: product.id,
name: product.title,
},
reviews: {
items: reviews.slice(0, 10),
},
recommendations: recommendations.map(rec => ({
id: rec.product_id,
name: rec.product_name,
})),
availability: {
quantity: stock.quantity,
}
};
}
}
- Клиенты микросервисов (Service Clients), которые специализируются на работе с каждым микросервисом, они формируют HTTP-запросы, обрабатывают ошибки и timeout.
BFF и Микрофронтенды
Когда мы работаем с микрофронтендами, то мы разбиваем монолитное приложение на части. Каждый отдельный микрофронтенд (MFE) разрабатывается и деплоится независимо, но должны работать вместе, как единое целое. Тут возникает несколько проблем: каждый MFE делает запросы напрямую к микросервисам, данная логика может дублироваться в разных MFE, что может привести к сложностям реализации MFE или же нарушению принципа независимости.
Когда мы используем BFF совместно с микрофронтендами, то существует несколько подходов:
1) Специализированный BFF для каждого MFE - каждый микрофронтенд имеет собственный BFF, они вместе разрабатывается и деплоятся.
2) Единый BFF с модульной структурой - единый BFF-сервер с четким разделением на модули для каждого MFE.
BFF + микрофрентенды имеют следующие преимущества:
- Полная независимость.
- Оптимизированная загрузка данных.
- Устойчивость к изменениям.
- Единая точка управления.
Данный подход позволяет работать независимо, ускоряет разработку и повышает надежность приложения. BFF - дает нам контроль над данными и позволяет создавать эффективные приложения.
BFF - это первый шаг к улучшению архитектуры, результат не заставит себя долго ждать.
Время стоить архитектуру, которая работает на вас, а не наоборот.
Top comments (0)