Найти тему
all about it

Ускоряем процесс интеграции между frontend и backend приложением (на основе swagger-схемы)

Оглавление

Бинго-бонго и джимбо-джанго!

Недавно, была поставлена задача, собрать MVP:

  • front-end приложение, состоящее из нескольких микро-фронтов (Build-time интеграция)
  • back-end приложение (bff + domain-service)

В добавок к этому были следующие ограничения:

  • разработка веб-апп должна быть на VueJS+TS
  • переиспользовать общие компоненты (дизайн система, сетевой слой, проверка прав пользователя)
  • использование контрактной системы
  • интеграция с новыми сервисами должна быть максимально быстрой

Рассмотрим упрощенный пример с аналогичным стеком, имеем

  • backend – FastAPI
  • front-end – VueJS + TS
-2

Как происходит обычно?

После того как было реализовано API на стороне backend, начинается процесс интеграции с ним. Как правило, это ручной процесс. Разработчик должен описать request-schema's, response-schema's, обработку http-ошибок, модели сущностей и так далее. Выглядит как монотонная и трудоемкая операция. Не стоит огорчаться раньше времени

Как вы знаете, в FastAPI одной из фишек является возможность описывать сущности, формат запросов и ответов используя pydantic + автоматически генерировать документацию для API-методов в swagger-схему. Хм, давайте подумаем как можно использовать готовое описание апи в дальнейшем?

Решение проблемы

Одно из решений проблемы – "слепить" наши два приложения в одно, используя jinja2, благо в FastAPI есть такая возможность. Но таким образом мы получаем ворох проблем:

  • монолитное приложение
  • сложно распараллелить работу команды, тк есть связанные компоненты
  • сложность внесения отдельных изменений
  • сложность сборки, поддержки зависимостей
  • сложность "разделения" в будущем на отдельные два приложения
  • сложность доставки/отката сборки приложения (деплой)
  • сложность доставки и ускорения раздачи "статики"
  • данное решение противоречит поставленной задаче

Получается все как-то коряво и не то. Где наши самостоятельные микро-сервисы? Где новые технологии? Со временем такой проект станет доставлять больше негативных эмоций не только разработчикам, но и BO, PO, аналитикам, в конце концов команде сопровождения. В общем имеем всем знакомые проблемы при работе с монолитом (но монолиты и опыт работы с ними бывает и положительный).

Решаем проблему со вкусом

Итак, у нас имеется swagger-схема. Как мы знаем, сообщество swagger очень большое, да в целом имеет большое кол-во вспомогательных инструментов. Один из таких – swagger-codegen. Говоря простыми словами – он позволяет из OpenAPI (swagger-схемы), используя подход кодо-генерации, подготовить библиотеку-"клиента". Один из ярких примеров использования такого подхода – python клиент для взаимодействия с апи kubernetes

https://github.com/kubernetes-client/python/blob/master/kubernetes/client/api_client.py
https://github.com/kubernetes-client/python/blob/master/kubernetes/client/api_client.py

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

Для начала необходимо установить openapi-generator, подробный процесс установки описан тут.

Приступаем к сборке клиента

Собираем клиента для Python

openapi-generator generate -i https://<host>:<port>/openapi.json -g python --additional-properties=packageName=<repository-name (сокращенный)>_api,library=asyncio -o <path>

Собираем клиента для TypeScript

Тут имеются небольшие отличия (подробное описание возможных параметров - тут)

openapi-generator generate -i https://<host>:<port>/openapi.json -g typescript-axios --additional-properties=packageName=<repository_name(сокращенный)>_api,withSeparateModelsAndApi=true,modelPackage=models,apiPackage=api -o <path>

  • withSeparateModelsAndApi - разделять хранение апи и модели в разных директориях
  • modelPackage – где будут лежать модели
  • apiPackage - где будут лежать апи-методы
структура полученного клиента
структура полученного клиента
пример сгенеренной модели response для профиля пользователя
пример сгенеренной модели response для профиля пользователя

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

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

  • для версионирования выбираем любой удобный и принятый в вашей команде инструмент (git tag, файл version.txt, comitizen) + интегрируем этот шаг в наш пайплайн в gitlab-ci.yml (в моем случае это был файл version.txt, передаваемый в виде артефакта между стадиями пайплайна)
  • шаг с запуском линтеров
  • шаг установки необходимых зависимостей
  • шаги для генерации и публикации собранных библиотек

-6
-7

Теперь про важное. Внимательный читатель обратил внимание на последний скрин-шот и задался вопросом: а куда, собственно, мы публикуем собранные клиенты? Тут все зависит от того, что имеется в вашей компании, команде – nexus, npm-registry, gitlab package registry, e.t.c. Если ранее не было опыта работы с такими системами, то советую ознакомиться с npm-registry в gitlab package registry.

В целом это все. После того как настроили публикацию собранного клиента, остается только добавить собранную библиотеку в качестве зависимости в front-end приложении.

Заключение

Мы проделали немалую работу, которая в будущем скажется на процессе интеграции между сервисами.

Что мы имеем:

  • backend приложение имеет swagger-схему aka документацию
  • на основе swagger-схемы мы можем автоматически генерировать библиотеку-клиента под любой ЯП
  • каждая сборка клиента имеет свою версию
  • использование контрактной системы между микро-сервисами
  • наличие готовых клиентов – ускоряет процесс интеграции и разработки