Как Vrbo разработал свою компонентную модель для GraphQL

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

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

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

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

Цели высокого уровня

При разработке новой архитектуры API переднего уровня (оркестровка, общедоступные API) мы преследовали несколько целей.

  1. Повышение скорости работы команды за счет уменьшения зависимости между командами и службами
  2. Уменьшите радиус взрыва за счет:
    * изоляции команд от изменений
    * уменьшения количества точек отказа
  3. Повысьте предсказуемость команды:
    * вычислительные потребности
    * ожидаемые затраты
  4. Упростите совместную работу и совместное использование
  5. Повышение переносимости в разных средах

Разбиение схемы GraphQL на компоненты

Схема GraphQL состоит из типов (схема типов), корневых типов (операции API) и преобразователей (бизнес-логика).

Пример типа:

Это определяет новый тип Author, который является просто определением типа. Чтобы выполнять операции с Автором, такие как запрос, мы должны определить корневой тип:

Это определяет операцию запроса к поверхности API под названием author, с которой теперь могут взаимодействовать клиенты.

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

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

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

Наша первая итерация объединения схем, которая до сих пор была основой нашего успеха, называется «частичными». Это принесло в GraphQL удобство и простоту узловых модулей.

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

Принципы проектирования реализации

При разработке инструментов для создания схем на основе компонентов нам нужны следующие характеристики:

  • Составные типы
    * Типы могут состоять из множества общих типов
    * Расширять и составлять типы для одного варианта использования, не затрагивая другие
  • Составные преобразователи
    * Вызов без вызова новой службы (без сетевых переходов)
    * Возможность уменьшить количество повторяющихся вызовов службы в одном дереве запросов
  • Переносимость
    * Вводимые требования восходящего потока (сервисные клиенты и т. д.)
  • Простота совместной работы
    * Общий код, а не сервисы
    * Управляйте вкладом, изменяйте версию по мере необходимости, упрощайте совместную разработку
  • Сначала схема
    * Легко читать, легко обновлять
    * Совместная кросс-платформенная разработка API на общем языке

Отдельные варианты использования, отдельные сервисы

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

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

  1. Выбирайте и выбирайте свои собственные потребности в оркестровке
  2. Версия независимо от других команд
  3. Независимое масштабирование в соответствии с требованиями сценария использования
  4. Лучшая атрибуция затрат
  5. Явно объявляйте требования к данным в компонентах пользовательского интерфейса

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

Состав

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

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

Компонентный модуль A может состоять из импортированного модуля и типа T, тогда как другой компонентный модуль B с немного другими потребностями, чем A состоит из компонента T '.

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

Компоненты в коде

Итак, как это может выглядеть с точки зрения кода? Давайте рассмотрим более реальный пример, когда объявление о недвижимости появляется на таком сайте, как Vrbo.

Из чего состоит объявление? Давайте сделаем это слишком просто и предположим:

  • Имущество
  • Отзывы

Начнем с компонента "Свойство".

Далее, компонент "Обзоры":

Наконец, давайте объединим их в листинг:

Это отличается, потому что в нем также есть новое объявление для импорта property и review. Это позволяет листингу использовать преимущества обоих типов в этих двух компонентах, а также преобразователей.

В этом последнем примере давайте также взглянем на преобразователи списков:

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

Остальная часть формирования типа Listing выполняется с помощью преобразователей типов (здесь не показаны).

Сотрудничество

GraphQL - это как язык запросов, так и спецификация API на основе схемы на основе типов.

Одна из проблем (а иногда и преимуществ) REST заключается в том, что он, естественно, не является схемой. Такие инструменты, как спецификация OpenAPI, часто полагаются на создание спецификации постфактум только для целей документации; строгой привязки схемы API к реализации нет.

В GraphQL схема - это API, и это мощная вещь.

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

Обычный тест и даже отдельные файлы .graphql для определений типов и корневых типов намного проще читать и работать с ними совместно, чем с кодом, не говоря уже о том, что они не зависят от какого-либо конкретного языка или платформы.

Сохранение синхронизации

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

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

Наконец-то

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

Когда доходит до того, что нужно для разработки современного веб-приложения в Vrbo, это выглядит примерно так:

Разработчики проводят свое время в двух областях: разработка на React и разработка на GraphQL (который обычно используется повторно). Возникает вопрос: зачем мы вообще развертываем приложения?

С появлением новых возможностей в сетях CDN, таких как вычисления с помощью CloudFlare Workers, Fly.io и других, бессерверный (и даже бесконтейнерный) уровень оркестровки имеет большой смысл.

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

дальнейшее чтение

[Обновление] Мы выпустили graphql-component для нашей организации с открытым исходным кодом Expedia Group.

Вы также можете узнать больше об истории по этим ссылкам:

Пока мы работали над следующей итерацией нашей частичной модели схемы / компонента, был выпущен новый проект с открытым исходным кодом под названием Модули GraphQL. Модули GraphQL следуют почти идентичной парадигме и тоже отлично выглядят.