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

Шаблон Backends-for-Frontends (BFF) — это интересное решение проблемы, с которой сталкиваются многие команды, — значительное отделение внешнего интерфейса от внутреннего, ограждение первого от изменений второго.

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

Итак, приступим!

Что такое шаблон BFF?

BFF означает Backend For Frontend, и это очень умный способ спроектировать ваше приложение таким образом, чтобы независимо от того, сколько различных интерфейсов вы предлагаете, основные серверные службы оставались нетронутыми.

Что это делает по-другому? Что ж, иногда вы можете предлагать очень разные интерфейсы — несколько приложений, которые предназначены для повторного использования ваших серверных сервисов, при этом каждое из них предлагает вашим пользователям совершенно разные UX.

Подумайте об ограниченном мобильном UX, многофункциональном настольном приложении или интерфейсе с отчетами по SMS. Это три совершенно разных UI/UX, в основе которых лежит одна и та же бизнес-логика. Но если вы попытаетесь создать уникальный и общий набор микросервисов, способных удовлетворить все потребности пользовательского интерфейса, вы рискуете создать ненужную связь между интерфейсом и сервером. Это всегда приводит к раздутым и сложным в обслуживании кодовым базам и утечке бизнес-логики во внешний интерфейс.

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

И как мы это решим?

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

Однако с правой стороны, с BFF, вы можете видеть, как реализация нового «бэкэнд-слоя» для каждого UX помогает упростить клиентские приложения, разрывая тесную связь между внешним и внутренним сервисами (будь то внутренние или внешние API). , что позволяет вам обновлять и изменять их по своему усмотрению, сохраняя при этом неповрежденным код на стороне клиента (соответственно, вы просто впитываете изменения в BFF).

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

№1. Создайте один BFF для каждого пользовательского опыта

Основная роль BFF должна заключаться в удовлетворении потребностей своего конкретного внешнего клиента — и не более того.

Посмотрите на диаграмму выше, микросервисы все еще там, они владеют «основной бизнес-логикой». Дополнительный уровень, добавленный BFF, предназначен только для решения проблем, характерных для каждого UX.

Что я имею в виду? В приведенном выше примере я предложил 3 разных клиента для наших API:

  • SMS
  • Мобильный
  • Настольное приложение

Каждый из них обеспечивает уникальный пользовательский опыт, но здесь важно то, что мы говорим об удовлетворении конкретных потребностей на основе предоставленного UI/UX, а не конкретных потребностей каждой отдельной клиентской реализации.

Даже если бы, например, было 2 мобильных приложения (одно для Android и одно для iOS), они все равно обеспечивали бы (более или менее) одинаковый UI/UX и имели бы схожие потребности в данных, поэтому вам нужно было бы только одно « мобильный опыт» лучшая подруга, а не одна лучшая подруга для Android и одна для iOS.

Выполнение этого для каждого клиента приведет только к дублированию кода без уважительной причины. Существует существенная разница между «потребностями конкретного UI/UX» и «потребностями конкретного клиента», которые мы должны помнить при разработке наших BFF.

№ 2. Не изобретайте велосипед

Внедрение шаблона BFF может быть как сложным и сложным, так и простым и тривиальным, как вы хотите.

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

В конце концов, вам нужен слой абстракции над вашими микросервисами. Одним из таких фреймворков является WunderGraph.



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

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

Первый. Давайте создадим пример приложения NextJS с включенным WunderGraph:

npx create-wundergraph-app my-weather --example nextjs

Перейдите в папку my-weather и запустите npm i . Как только это будет сделано, запустите сервер с npm start.

Вы увидите страницу, на которой отображается информация о паре капсул SpaceX Dragon (в качестве примера WunderGraph использует API SpaceX GraphQL). Не обращайте на это внимания и перейдите в файл .wundergraph/wundergraph.config.ts, удалите ссылку на SpaceX API и отредактируйте ее, чтобы вместо этого она выглядела так:

//...
const weather = introspect.graphql({
  apiNamespace: 'weather',
  url: 'https://weather-api.wundergraph.com/',
});
const countries = introspect.graphql({
  apiNamespace: 'countries',
  url: 'https://countries.trevorblades.com/',
});
// configureWunderGraph emits the configuration
configureWunderGraphApplication({
 apis: [weather, countries],
 //...
 },
 //...
});

По сути, я удалил все ссылки на API SpaceX и добавил 2 новые интеграции API, weather и countries API, а затем добавил их в массив apis dependency.

Этот файл содержит конфигурацию вашего сервера WunderGraph BFF. Здесь вам нужно добавить все API, которые вы хотите составить вместе, и это даже не обязательно должны быть зависимости данных, такие как микросервисы, базы данных или внешние API. WunderGraph также поддерживает интеграцию аутентификации через Clerk, Auth.js или ваше собственное решение для аутентификации; и даже S3-совместимое хранилище для загрузки файлов. Все запечено в слое BFF.

Теперь вы можете перейти в папку .wundegraph/operations и создать файл с именем CountryWeather.graphql, куда мы добавим удобную операцию GraphQL, объединяющую информацию из обоих API.

query ($countryCode: String!, $capital: String! @internal) {
  country: countries_countries(filter: { code: { eq: $countryCode } }) {
    code
    name
    capital @export(as: "capital")
    weather: _join @transform(get: "weather_getCityByName.weather") {
      weather_getCityByName(name: $capital) {
        weather {
          temperature {
            max
          }
          summary {
            title
            description
          }
        }
      }
    }
  }
}

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

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

const weather = useQuery( {
  operationName: 'CountryWeather',
  input: {
   countryCode: 'ES'
  }
 })

И в результате я получу вот такой JSON:

{
   "data":{
      "country":[
         {
            "code":"ES",
            "name":"Spain",
            "capital":"Madrid",
            "weather":{
               "temperature":{
                  "max":306.68
               },
               "summary":{
                  "title":"Clear",
                  "description":"clear sky"
               }
            }
         }
      ]
   },
   "isValidating":true,
   "isLoading":false
}

Использовались оба API, их ответы собирались и сшивались вместе, но с точки зрения разработчика внешнего интерфейса им нужно было только вызвать конечную точку на сервере BFF — ту, которая использует постоянный хешированный запрос GraphQL для получения данных, необходимых UI/UX клиента, предоставляя окончательный ответ в виде JSON через RPC.

Это как волшебство! (Вы можете получить полный исходный код для этого проекта здесь, если хотите рассмотреть его подробно).

№3. Остерегайтесь антипаттерна разветвления

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

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

Потенциальные решения этих проблем включают:

  1. Шаблон автоматического выключателя. С помощью этого шаблона вы можете контролируемо обрабатывать неисправные и отказывающие серверные службы. Когда у серверной службы возникают проблемы, прерыватель цепи быстро предотвращает отправку BFF дополнительных запросов, сокращая время, затрачиваемое на ожидание неотвечающих служб. Изолируя таким образом сбойную внутреннюю службу, прерыватель цепи предотвращает влияние каскадных сбоев на другие части BFF или внешнего приложения. Это обеспечивает плавную деградацию при недоступности серверной части.
  2. Кэширование. Если серверная служба недоступна, BFF может предоставить кэшированные данные или данные по умолчанию вместо возврата ошибки. Кэширование часто используемых данных также может помочь снизить зависимость от серверных служб и сократить время отклика в периоды недоступности серверной части, особенно при использовании в сочетании с автоматическим выключателем.
  3. Мониторинг услуг. Если вы хотите понять, как используются ваши сервисы (количество запросов, наиболее распространенные запросы и т. д.), то уровень BFF – отличное место для ведения журнала, мониторинга работоспособности серверных сервисов и отслеживания. их доступности и производительности. Самое приятное то, что вы можете заранее выявлять и решать проблемы с помощью этой информации.

№ 4. Постоянно обрабатывать ошибки на BFF

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

Другими словами, воспользуйтесь преимуществами слоя BFF и используйте его в качестве агрегатора/транслятора ошибок. Клиентское приложение не может разобраться в HTTP 500 и HTTP 200 с ошибкой в ​​теле JSON — и не должно.

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

№ 5. Используйте сервер на основе узла, чтобы вы могли использовать TypeScript

Сервер на основе NodeJS (Express.js, NestJS или просто реализация BFF от WunderGraph, которая использует уровень сервера Node/Go) для вашего BFF позволяет использовать TypeScript как во внешнем, так и во внутреннем интерфейсе. Оцените его преимущества!

TypeScript — это расширенный набор JavaScript, который добавляет статическую типизацию, что добавляет уровень безопасности, помогая вам обнаруживать ошибки на ранних этапах разработки и делая ваш код более удобным для сопровождения и более простым для рефакторинга. Если вы можете использовать TypeScript для разработки как внешнего, так и внутреннего интерфейса (уровень BFF), вы достигаете языковой согласованности во всем приложении, облегчая разработчикам переключение между клиентским и серверным кодом без переключения контекста между языками.

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

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

Вот статья, которую я написал некоторое время назад о том, как это сделать.

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

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

Вы когда-нибудь использовали этот шаблон раньше? А как насчет WunderGraph как фреймворка BFF? Делитесь своими мыслями в комментариях, мне будет интересно узнать, что вы о них думаете!