Ко всем новостям

Объединение сервисов в GraphQL c помощью Apollo Federation и Schema Stitching в Platform V DataSpace

Публикации в СМИ
Технологии
11.12.2023

Источник: Хабр "GraphQL и микросервисная архитектура: объединяем сервисы в федерацию"

16125d32c78db5c9743a261ab8882570.png

Меня зовут Владислав Гончаров, я разработчик в команде Platform V DataSpace СберТеха. Расскажу, как мы решаем вопрос с объединением сервисов в GraphQL и микросервисной архитектуре, которая позволяет разбить любое большое приложение на маленькие сервисы. С одной стороны, их проще написать и поддерживать небольшой командой. А с другой — некоторые задачи теперь требуют выполнения сразу нескольких запросов вместо одного.

Например, возьмём банковское приложение. Его можно разбить на сервисы:

  • «Клиенты» — сервис, хранящий данные о клиентах;
  • «Счета» — сервис, хранящий данные о счетах (при сохранении этой информации потребуется идентификатор клиента);
  • «Кредиты» — сервис, хранящий данные о кредитах (при сохранении этой информации потребуется идентификатор счёта), и др.

Предположим, что нам нужно получить информацию о кредите (сумма займа и ФИО заёмщика) по номеру кредитного договора. В микросервисной архитектуре алгоритм будет такой:

  1. Через сервис «Кредиты» найти кредит по номеру кредитного договора и получить сумму займа и идентификатор счёта.
  2. Через сервис «Счета» найти счёт по его идентификатору и получить идентификатор владельца (клиента).
  3. Через сервис «Клиенты» найти клиента по его идентификатору и получить ФИО.

Распишем этот алгоритм в виде GraphQL‑запросов.

Поиск кредита по номеру кредитного договора («АА №000001») и получение суммы займа и идентификатора счёта

Запрос:

query { searchCredit(cond: "it.contractNumber == 'AА №000001'") { elems { loanAmount account { entityId } } } }

Ответ:

{ "data": { "searchCredit": { "elems": [ { "loanAmount": 1000000, "account": { "entityId": "7088959748502519809" } } ] } } }

Поиск счёта по идентификатору («7088959748502519809») и получение идентификатора владельца (клиента)

Запрос:

query { searchAccount(cond: "it.$id == '7088959748502519809'") { elems { owner { entityId } } } }

Ответ:

{ "data": { "searchAccount": { "elems": [ { "owner": { "entityId": "7088959395241394177" } } ] } } }

Поиск клиента по идентификатору ("7088959395241394177") и получение ФИО

Запрос:

query { searchClient(cond: "it.$id == '7088959395241394177'") { elems { lastName firstName patronymic } } }

Ответ:

{ "data": { "searchClient": { "elems": [ { "lastName": "Иванов", "firstName": "Иван", "patronymic": "Иванович" } ] } } }

Для решения задачи нужно сделать несколько запросов к различным сервисам. Можно также отметить однообразные действия при поиске сущностей по идентификаторам из ссылок, которые неплохо было бы упростить. Давайте воспользуемся Apollo Federation и Schema Stitching.

Apollo Federation — это open source-решение, которое позволяет реализовать в GraphQL микросервисный подход. Вместе со Schema Stitching, Apollo Federation позволяет сделать сервис, который имеет в качестве GraphQL-схемы объединение GraphQL-схем нескольких сервисов. Благодаря этому ссылочные поля, которые были просто идентификаторами на GraphQL-схеме отдельного сервиса, становятся полноценными ссылками.

Вот что получилось в результате — рассмотрим на примере задачи по поиску кредита.

Поиск кредита по номеру кредитного договора ("АА №000001") и получения суммы займа и ФИО заёмщика

Запрос: 

query { searchCredit(cond: "it.contractNumber == 'AА №000001'") { elems { loanAmount account { entity { owner { entity { lastName firstName patronymic } } } } } } }

Ответ:

{ "data": { "searchCredit": { "elems": [ { "loanAmount": 1000000, "account": { "entity": { "owner": { "entity": { "lastName": "Иванов", "firstName": "Иван", "patronymic": "Иванович" } } } } } ] } } }

Объединение сервисов в DataSpace

Вот как на практике выглядит реализация сервиса с объединённой GraphQL-схемой сервисов DataSpace.

Описание сервисов

В первую очередь описываем модели наших сервисов:

Снимок экрана 2024-03-27 в 15.00.41.png
Клиенты.
Снимок экрана 2024-03-27 в 15.01.14.png
Счета.
d11c98dbaf05fe8afaae78a62823d89d.png
Кредиты.

Выпуск сервиса с объединённой GraphQL-схемой сервисов «Клиенты», «Счета» и «Кредиты»

Сервис с объединённой GraphQL‑схемой выпускается в рамках одного из объединяемых сервисов. В качестве такового выберем сервис «Кредиты». Подключим к нему внешние модели (сервисов «Счета» и «Клиенты»):

86537792941b38e365c195f816eaa4dc.png
Нажимаем кнопку «Внешние модели».
Снимок экрана 2024-03-27 в 15.03.20.png
Выбираем из списка имя модели сервиса «Счета».
Снимок экрана 2024-03-27 в 15.04.11.png
Нажимаем кнопку «Добавить».
c90b619b5d629b99fb6e0950e850b799.png
Проделываем то же самое для модели сервиса «Клиенты».
613d3a4ddce6ab2f71e381c540781a50.png
Затем выпускаем сервис — готово!

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

Примечание 2. Имя модели можно посмотреть в параметрах модели.

193a8627d3a9d4f8ff85df0968ee0639.png

Примечание 3. На момент выпуска сервиса должны быть выпущены сервисы подключённых внешних моделей .

Теперь мы можем перейти на вкладку «Детали» и, помимо endPoint'ов сервиса «Кредиты», увидим endPoint сервиса с объединённой GraphQL‑схемой.

452d9eff5f8fb4524be9f4b39a40e258.png

А на вкладке «GraphQL‑конструктор» можно выполнить GraphQL‑запрос для сервиса с объединённой GraphQL‑схемой (поставив соответствующую галочку напротив «GraphQL Stitching»).

06284b7a9ca8c62c6c19b82f74b885ae.png

Примечание

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

Точно так же поступили с поиском: в GraphQL для этого нет инструментов, максимум — поиск по ID или нестандартизованная пагинация. В DataSpace для этого реализован целый набор инструментов, доступный на уровне GraphQL и позволяющий осуществлять пагинацию, сортировку и группировку. О том, как и зачем это было сделано, расскажем в следующем материале. Спасибо за внимание!