![](https://habrastorage.org/getpro/habr/upload_files/161/25d/32c/16125d32c78db5c9743a261ab8882570.png)
Меня зовут Владислав Гончаров, я разработчик в команде Platform V DataSpace СберТеха. Расскажу, как мы решаем вопрос с объединением сервисов в GraphQL и микросервисной архитектуре, которая позволяет разбить любое большое приложение на маленькие сервисы. С одной стороны, их проще написать и поддерживать небольшой командой. А с другой — некоторые задачи теперь требуют выполнения сразу нескольких запросов вместо одного.
Например, возьмём банковское приложение. Его можно разбить на сервисы:
«Клиенты» — сервис, хранящий данные о клиентах;
«Счета» — сервис, хранящий данные о счетах (при сохранении этой информации потребуется идентификатор клиента);
«Кредиты» — сервис, хранящий данные о кредитах (при сохранении этой информации потребуется идентификатор счёта), и др.
Предположим, что нам нужно получить информацию о кредите (сумма займа и ФИО заёмщика) по номеру кредитного договора. В микросервисной архитектуре алгоритм будет такой:
Через сервис «Кредиты» найти кредит по номеру кредитного договора и получить сумму займа и идентификатор счёта.
Через сервис «Счета» найти счёт по его идентификатору и получить идентификатор владельца (клиента).
Через сервис «Клиенты» найти клиента по его идентификатору и получить ФИО.
Распишем этот алгоритм в виде 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.
Описание сервисов
В первую очередь описываем модели наших сервисов:
![Клиенты. Клиенты.](https://habrastorage.org/getpro/habr/upload_files/771/8f3/4a2/7718f34a20cfe38dc171288753c20847.png)
![Счета. Счета.](https://habrastorage.org/getpro/habr/upload_files/70c/8f5/172/70c8f5172be27ef4c67028329ea68f77.png)
![Кредиты. Кредиты.](https://habrastorage.org/getpro/habr/upload_files/d11/c98/dba/d11c98dbaf05fe8afaae78a62823d89d.png)
Выпуск сервиса с объединённой GraphQL-схемой сервисов «Клиенты», «Счета» и «Кредиты»
Сервис с объединённой GraphQL‑схемой выпускается в рамках одного из объединяемых сервисов. В качестве такового выберем сервис «Кредиты». Подключим к нему внешние модели (сервисов «Счета» и «Клиенты»):
![Нажимаем кнопку «Внешние модели». Нажимаем кнопку «Внешние модели».](https://habrastorage.org/getpro/habr/upload_files/865/377/929/86537792941b38e365c195f816eaa4dc.png)
![Выбираем из списка имя модели сервиса "Счета". Выбираем из списка имя модели сервиса "Счета".](https://habrastorage.org/getpro/habr/upload_files/025/fbd/014/025fbd014f0bc35d0eaea6fbbd5b84d2.png)
![Нажимаем кнопку «Добавить». Нажимаем кнопку «Добавить».](https://habrastorage.org/getpro/habr/upload_files/2e8/8da/738/2e88da738d2f126251b27bd18e088550.png)
![Проделываем то же самое для модели сервиса «Клиенты». Проделываем то же самое для модели сервиса «Клиенты».](https://habrastorage.org/getpro/habr/upload_files/c90/b61/9b5/c90b619b5d629b99fb6e0950e850b799.png)
![Затем выпускаем сервис — готово! Затем выпускаем сервис — готово!](https://habrastorage.org/getpro/habr/upload_files/613/d3a/4dd/613d3a4ddce6ab2f71e381c540781a50.png)
Примечание 1. Можно подключать внешние модели из пространств других пользователей, к которым были предоставлены доступы.
Примечание 2. Имя модели можно посмотреть в параметрах модели.
![](https://habrastorage.org/getpro/habr/upload_files/193/a86/27d/193a8627d3a9d4f8ff85df0968ee0639.png)
Примечание 3. На момент выпуска сервиса должны быть выпущены сервисы подключённых внешних моделей .
Теперь мы можем перейти на вкладку «Детали» и, помимо endPoint'ов сервиса «Кредиты», увидим endPoint сервиса с объединённой GraphQL‑схемой.
![](https://habrastorage.org/getpro/habr/upload_files/452/d9e/ff5/452d9eff5f8fb4524be9f4b39a40e258.png)
А на вкладке «GraphQL‑конструктор» можно выполнить GraphQL‑запрос для сервиса с объединённой GraphQL‑схемой (поставив соответствующую галочку напротив «GraphQL Stitching»).
![](https://habrastorage.org/getpro/habr/upload_files/062/84b/7a9/06284b7a9ca8c62c6c19b82f74b885ae.png)
Примечание
Стоит добавить, что GraphQL сам по себе не поддерживает наследование сущностей, но в DataSpace мы добавили такую возможность. Например, инструмент позволяет запросить сущность или уточнить у дочерних сущностей дополнительные поля.
Точно так же поступили с поиском: в GraphQL для этого нет инструментов, максимум — поиск по ID или нестандартизованная пагинация. В DataSpace для этого реализован целый набор инструментов, доступный на уровне GraphQL и позволяющий осуществлять пагинацию, сортировку и группировку. О том, как и зачем это было сделано, расскажем в следующем материале. Спасибо за внимание!