Pull to refresh

Comments 40

Говорите про «без костылей», но первое же, о чем рассказываете — это о том, как написали прокси на ноде, чтобы не общаться напрямую с бэкендом. Теперь у вас становится в 2 раза больше апишек, к которым можно обращаться — «настоящий» апи для мобилок и прочего, и свой отдельный (и наверняка открытый наружу, в том числе для мобилок) апи, который «чуть-чуть отличается» и почти полностью дублирует функциональность «настоящего» апи, но делает все немного по-своему. Мне одному кажется, что это, мягко говоря, не лучшее решение?
А если бэкендов несколько? Подход хороший для определенного скоупа задач.
Апишек не становится в 2 раза больше. В 99% случаев это чистая прокся – один к одну с «настоящим» бэкендом. В редких случаях, когда batching’а недостаточно, приходится агрегировать несколько ручек. Но это не костыль, так работает REST.
И только в самом крайнем случае, когда нет других вариантов (например, это внешнее API, которое вы никак не контролируете) приходится немножко править ответ.
А когда на это апи завяжется другое, которое тоже удумает проксировать запросы, и после этого что-то изменят в изначальном апи — отлетит всё расходящееся дерево проксей?
Почему отлетит? Все роуты документированные. Приведу на примере, есть сервис с Юзерами, и есть сервис с Постами. Что б каждый раз руками не писать http запрос на юзер сервис, или на пост сервис, делаем допустим миддлварку для Юзер префикса, которая просто редиректит запрос и хендлим респонс как хотим. Если мы моддифицируем наш респонс, то это тоже документируется. Эта прокся просто лаер между всеми микросервисами и фронтом
BFF в данном виде может служить или уже служит как api-gateway, это не костыль, а частая практика, особенно для микросервисов
Симптомы проблемы

Это симптомы алкоголизма или наркомании у того, кто пишет такое апи. На лечение отправьте.


Итак, какие могут быть пути решения этой проблемы? Что мы как фронтендеры можем сделать на своей стороне, чтобы работать с API приемлемого качества?

шаг 1: требование от человека, который "апи принес", предоставлять следующий раз нормальное апи
шаг 2: если шаг 1 не оказался успешным — обращение к начальству с требованием разобраться
шаг 3: если шаг 2 не оказался успешным, увольняться нафиг

Спасибо за интересную статью. Мы тоже используем BFF, поэтому интересно узнать ваше мнение.

— Как вы тестируете соответствие ответа бэкенд АПИ и его Swagger документации? Каки-либо контракт-тесты используете?
— Как часто валидация ответа по JSON-схеме помогало находит ошибки на проде?
— Валидация синхронная, не было у вас проблем с блокированием event-loop при валидации большого ответа?
– После каждого релиза бэкендеров мы гоняем тесты на соответствие ответов API и Swagger-документации. Данные для формирования запросов берем из самого свагера, обычно это поле x-example.

– Валидация ответов в продакшене очень редко находит баги, т.к. есть несколько ступеней защиты.
Во-первых, после каждого релиза бэкенда надо снапшотить Swagger-схему и проверять, что новая схема является надмножеством старой (дорелизной) схемы. Таким образом мы можем удостовериться, что релиз бэкенда не имеет ломающих изменений, а если имеет, то мы к ним готовы.
Во-вторых, из Swagger’а можно генерировать Flow-типы и смотреть не ругается ли Flow на ваш код.

– Мы утешаем себя тем, что ajv очень быстрый, плюс у нас куча инстансов ноды, балансер и все такое.
Но если это становится проблемой, то можно отключить (полностью или частично) валидацию в проде, оставить только в престейбле.
А какой инструмент вы используете для генерации Flow-типов?
У нас свой внутренний инструмент, он не в опен-сорсе, к сожалению.
Спасибо, полезная статья. За линк на валидатор схем — отдельное спасибо, ибо это больной вопрос.
При этом фейкер дружит со свагером, и вы можете спокойно поднять моковый сервер, который на основе Swagger-схемы будет генерировать вам фейковые ответы по нужным путям.


улыбнуло предложение :)
У вас была задача на добавление/обновление множества элементов? Расскажите как ее решили.
год назад начали писать новый проект, решили сделать api на основе вебсокетов, два типа запросов: по айдишнику или коллецией. Получилось очень удобно, фронтенд грузит все, показывает как ему удобно, не нужно на каждую хотелку менять апи, за год только dto потолстели чуток. Я прям народоваться не могу такому подходу
Logux не смотрели? Если конечно у вас Redux на клиенте.

Redux, но logux не

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

Мы используем swagger-codegen для гарантии того, что бэкенд формирует правильный ответ. К сожалению, в openapi 2.0 есть целый класс вещей, которые невозможно реализовать, а фронтенд их очень хочет, так что многие вещи приходится делать нелегально. Например, сущность или массив в query параметре.

Кое-что было поправлено в openapi 3.0, но с ним своя беда. Во-первых, отвратительная поддержка тулзами. Тот же openapi-codegen хотя формально и поддерживает новую спеку, ужасно забагован и для продакшена пока не годится. Во-вторых, openapi 3.0 по формату очень не похож на openapi 2.0. Это может и не страшно для маленьких api, но у нас есть спеки по ~12 тысяч строк, которые грамотно отконвертить тоже будет непросто.

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

Какие библиотеки и фреймворки вы порекомендуете использовать для реализации подхода и что придется кодить самим?

Какие-то очевидные вещи описаны. Начал делать веб-приложение с нуля год назад. Серверная часть уже существовала лет 15. Сделал прокси как раз для этих очевидных вещей типа агрегации запросов и т.д. Прокси на .net. Свагером генерю сразу клиента на тайпскрипте. Детский сад.

Только в яндексе могут написать статью, используя специфичный сленг («ручки») и вместо того чтобы инлайн пояснить — сделать ссылку на стековерфлоу где поясняют что это специфичный яндексовый сленг и что он означает.
Имхо, Яндекс это такая компания, которая может только в крайности. Либо примитивы из костылей и велосипедов, либо махровый оверинжиринг (приготовление кофе и отправка космонавтов на луну прилагаются). Этим многие страдают, тот же фб, например. Но как то странно, что крупная компания не может делать сбалансированные, универсальные решения (да-да, работать с яндексом вне инструментария обозначенного в доках — лютейший ад, этот факт упорно не проникает в головы маркетологов).
Из-за чего вообще возникла насущная потребность в BFF? Команда, пишущая бэк, полностью оторвана от команды, пишущей фронт? Почему бэкэнд сразу не пишется так, чтобы фронтэндерам не было больно при интеграции? Где элементарные правила, которых должны придерживаться разработчики бэка и которыми можно сильно ударить по пальцам бэкэндера, который:
  • извращается с регистром полей в ответе (можно не любить конкретно underscored_case, например, но правила есть правила, единообразие важнее творческого самовыражения и личного мнения! в конце концов, командная работа же!);
  • извращается с форматом возвращаемого результата (массив, объект или null как результат ответа на один и тот же запрос, который возвращает список — серьезно? как такого на работу взяли?);
  • не пишет документацию по своим API (как минимум: имя, тип, текстовое описание каждого параметра запроса и поля в ответе + пример реального запроса и реального ответа на такой запрос).

То есть бэкэндеры у вас пишут как Б-г на душу положит, а фронтэндерам, чтобы как-то интегрировать в клиентскую часть это «творчество», приходится писать целую обертку (BFF), которая более или менее скрывает за собой всю убогость, несогласованность и противоречивость бэка? Я правильно понял?
Команда, пишущая бэк, полностью оторвана от команды, пишущей фронт? Почему бэкэнд сразу не пишется так, чтобы фронтэндерам не было больно при интеграции?

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

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


53 | feed_shoffed_id
54 | fesh
55 | filter-currency
56 | showVendors

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


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

но в статье же предлагается использовать BFF для того чтобы вот такие какашки: Превращать во что-то вменяемое.

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

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

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

И как компактность связана с тем, что в одном месте feed_shoffed_id, а в другом — filter-currency?


Поэтому бекенды вольны в выборе формата.

Конечно же свободен! Только в данном случае никакого формата нет, есть куча говна, которое бекенд вывалил под дверью у фронтенда. И если я под своей дверью вижу кучу говна, то:


  1. я ожидаю услышать объяснения, четкие и понятные — кто и почему навалил
  2. я ожидаю услышать извинения
  3. я уж точно не ожидаю, что предполагается, будто я его стану убирать. Кто намусорил — тот пусть за собой и прибирает.

Если же человек профнепригоден и не способен выполнять работу бекендщика — пусть увольняется нафиг.

Если изменения бэка не ломают совместимость (интерфейсы API, структуры данных и смысл полей) — фронт менять не нужно. А если нужно, значит совместимость (контракт) ломается. Почему не использовать версионирование API для этого? Фактически это оно и есть, но реализовано оно в BFF. То есть это такой двусторонний мультиадаптер с дополнительными функциями (кэширование, валидация и т.п.), сильно зацепленный и на фронт, и на бэк.
Версионирование АПИ предполагает, что рано или поздно фронтенд всё-таки придётся дорабатывать под новую версию. Или что-то не улавливаю в Вашем ответе?
Не обязательно. Достаточно просто не отключать версию бэка, которая используется таким фронтом. От фронта требуется лишь сообщить, какая версия API ему нужна (или, если он не сообщает, автоматом выставить какую-то версию по умолчанию). Другое дело, что для маршрутизации на нужную версию бэка нужен какой-то промежуточный слой (сервер Nginx, например, или тот же BFF).
А, хорошо, теперь понял. Но тогда — не получаем ли мы сложности в виде двух (или, реже, нескольких) версий бэка (решающих одну задачу!), которые надо поддерживать, и которые скорее всего ходят в одну базу. То есть, теперь изменения в базе нужно делать с оглядкой на две версии бэка.

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

В вебдеве изобрели паттерн «фасад»?

Почему пользователь в самом низу на картинке BFF?!) Картинка аж давит)
Переверните пирамидку

Уж не знаю где и спросить, попробую тут: Яндекс.Коннект вообще жив? Ничего не работает, поддержка не отвечает… Уже неделю не могу почту подключить, хотя операция простейшая. Во времена ПДД такого не было.
Ну такоооое, парни… По сути, вы прикрыли кучу мусора модным ковриком и написали про это статью.

Какие проблемы решены?

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


Какие проблемы остались нерешенными:

  • бэкенд все ещё творит в апи все, что хочет, и не задумывается над тем, в каком виде они отдают данные и в каком виде эти данные вообще нужны клиенту
  • процесс дебага усложнился — вместо того, чтобы посмотреть в серверном коде, почему отдаются неверные данные, теперь сначала нужно заглянуть в фасад — не там ли проблема?
  • появилась куча ручной работы — каждый новый серверный API необходимо дублировать
  • обработка исключений теперь тоже дублируется: отказать может как сервер, так и фасад
  • огромный недокументированный серверный API превратился в два огромных недокументированных API (добавился API фасада)
  • появился дополнительный overhead на обработку данных в фасаде, а значит дополнительные сервера, процессорное время etc.
  • добавление кеширующего слоя в фасаде, который не контролируется сервером, может привести к неожиданным багам, связанным с использованием устаревших данных


Что ещё забыл?

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

Но можно хотя бы сделать шаг в правильном направлении! У нас в компании ситуация была похожей ещё три-четыре года назад (с единственным исключением: у нас с самого начала был типизированный протокол, общий и для клиентов, и для сервера, в котором хотя бы поля были более-менее откомментированы). И тоже было огромное количество легаси, о котором знали буквально три старожила компании. Мы начали с того, что выделили отдельную команду, которая занималась тем, что проектировала API, описывала в документации сценарии его использования, обсуждала протокол как с серверными, так и с клиентскими разработчиками, и только когда все задействованные стороны согласны с проектом API и сопровождающей документацией — тогда изменения в протокол ехали в мастер и становились доступны как серверу, так и клиентам.

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

"Этот код выглядит ужасно. Потому что он ужасен. Но нам все равно нужно это сделать. У нас выбор: делать это на сервере или на клиенте. Я выбираю сервер." — глубокая фраза.)))

Sign up to leave a comment.