Это третья статья из цикла «Проектирование GraphQL API».
Введение
В предыдущих статьях мы рассмотрели основы GraphQL и принципы проектирования схемы. Теперь перейдём к архитектуре — фундаменту, определяющему, как GraphQL API будет работать в реальных условиях.
Архитектура GraphQL отличается от традиционных REST API. Вместо множества эндпоинтов используется единая точка входа. Это создаёт уникальные вызовы: как организовать код, обеспечить производительность, масштабировать систему и интегрировать различные источники данных.
Выбранная архитектура определяет:
Насколько легко будет развивать API;
Как система справится с нагрузкой;
Сложность поддержки и отладки;
Возможность командной работы.
В этой статье мы рассмотрим архитектурные паттерны GraphQL — от монолитных решений до федеративных систем. А также изучим основные принципы организации серверной части.
Мы не будем углубляться в типичные проблемы продакшена, такие как N+1 запросы, стратегии кеширования или защита от сложных запросов — эти важные темы будут рассмотрены в следующих статьях цикла.
Разберём, что такое GraphQL-сервер и как он устроен.
Как устроен GraphQL внутри
По сути GraphQL — это просто спецификация. Чтобы превратить её в работающий API, нужно реализовать серверную часть, которая сможет принимать запросы, проверять их и возвращать клиенту результат.
Сначала определимся с тем, что такое GraphQL-сервер, как он устроен и какие функции выполняет, а после рассмотрим, как он встраивается в жизненный цикл запроса.
GraphQL-сервер
GraphQL-сервер — это программа, которая служит средой выполнения для GraphQL-запросов.
То есть, GraphQL-сервер:
Принимает GraphQL-запросы от клиентов.
Проверяет их корректность.
Выполняет запросы (получает данные).
Возвращает ответ в нужном формате.
У каждого GraphQL-сервера есть несколько ключевых компонентов: схема, резолверы и контекст. Ниже рассмотрим каждый из них.
Схема
Схема описывает типы данных и операции (запросы, мутации и подписки), доступные клиентам. Это своего рода договор между сервером и клиентом: всё, что не описано в схеме, запросить нельзя. Мы подробно рассматривали схему во второй статье цикла.
Есть два подхода к работе со схемой:
Schema-first предполагает, что сначала пишется схема в SDL (специальном декларативном языке), а затем для неё реализуются резолверы.
Описание схемы перед разработкой облегчает совместную работу над API и создаёт единый источник правды.
Однако такой подход требует больше ручной работы по синхронизации схемы и кода.Code-first — противоположный подход: схема генерируется автоматически на основе кода. Это может быть быстрее в разработке и проще в поддержке. Однако на больших и сложных проектах такой подход может вносить путаницу.
Оба подхода активно используются, и выбор зависит от масштаба проекта, распределения ответственности между командами и предпочтений по стилю работы.
Резолверы
Это функции, которые отвечают за получение данных. Когда приходит запрос, сервер смотрит, какие поля нужно отдать, и для каждого поля вызывает соответствующий резолвер. Эти функции могут обращаться к базе данных, REST API, кэшам или любым другим источникам. Резолверам посвящён раздел в первой статье цикла.
На практике резолверы редко работают напрямую с «сырой» базой. Обычно между ними и хранилищем есть слой сервисов или репозиториев, которые инкапсулируют доступ к данным. Это делает архитектуру чище и позволяет легче тестировать и расширять код.
Контекст
Это объект, доступный всем резолверам при выполнении запроса. В нём обычно содержится информация о текущем пользователе, доступ к сервисам (например, к логированию, мониторингу или безопасности), а также подключения к базам данных и другим общим ресурсам. Контекст помогает сделать резолверы чистыми и независимыми — они не тянут всё, что им нужно, сами, а получают это из контекста.
Схема, резолверы и контекст — три базовых компонента любого GraphQL-сервера. Рассмотрим, как всё это организовать в рамках общего кода приложения.
Организация кода
Возможны разные варианты организации кода GraphQL-сервера в рамках приложения.
Например, GraphQL-сервер может быть реализован, как часть монолитного приложения:
my-app/
├── graphql/
│ ├── schema.graphql
│ ├── resolvers/
│ └── context.js
├── services/ (бизнес-логика)
├── models/ (работа с БД)
└── app.js (точка входа)
В этом случае GraphQL-сервер запускается вместе с основным приложением. Резолверы вызывают сервисы/модели напрямую.
GraphQL может быть выделен в отдельный сервис:
graphql-api/
├── schema.graphql
├── resolvers/
├── dataloaders/
└── server.js
main-app/ (отдельное приложение)
├── services/
└── models/
В этом случае GraphQL работает в отдельном процессе/контейнере, но использует те же модели БД. Такой подход особенно удобен в микросервисной архитектуре, где API-слой должен быть независимым. Он требует чуть больше усилий на интеграцию, но даёт лучшую изоляцию и масштабируемость.
Конкретная реализация GraphQL-сервера всегда зависит от выбранной библиотеки, языка программирования и архитектурных решений.
Популярные библиотеками для создания GraphQL-серверов:
Apollo Server (JavaScript)
GraphQL Yoga (JavaScript)
GraphQL Java
Graphene (Python)
Мы поверхностно разобрались в устройстве GraphQL-сервера, теперь давайте рассмотрим, как происходит выполнение GraphQL-запроса.
Жизненный цикл запроса
Чтобы лучше понять, как GraphQL-сервер работает на практике, посмотрим на жизненный цикл запроса.
Путь от клиента до хранилища данных проходит через транспортный сервер и GraphQL-сервер:

Между GraphQL-сервером и хранилищем может находиться приложение, реализующее запросы к БД.
Ответ проходит тот же путь в обратном порядке, меняясь и обогащаясь на каждом шаге.
Рассмотрим жизненный цикл запроса на примере HTTP+JSON:
Клиент → Транспорт
Клиент отправляет HTTP POST-запрос с телом, содержащим GraphQL-операцию и, при необходимости, переменные.Транспорт → GraphQL
Веб-сервер принимает запрос и через специальный плагин (middleware) передаёт его GraphQL-серверу. Middleware извлекает GraphQL-запрос из body, может использовать заголовки для формирования контекста (например, определить пользователя), и передаёт всё это GraphQL-движку.GraphQL
Выполняется парсинг и валидация запроса по схеме. То есть сервер проверяет, существуют ли запрошенные типы, поля и аргументы. Если валидация не пройдена, выполнение прекращается и клиенту возвращается ошибка. Только после успешной валидации начинается выполнение запроса.GraphQL → Источники
GraphQL строит дерево запроса и вызывает резолверы для полей. Резолверы могут обращаться к базам данных, REST API, другим микросервисам или кешу. Они вызываются параллельно для полей одного уровня, но последовательно для вложенных уровней.Источники → GraphQL
Источники отдают данные в GraphQL-сервер, где собирается финальный результат.GraphQL → Транспорт
Данные проходят этап форматирования: строится JSON-ответ в соответствии со структурой запроса и типами, определёнными в схеме.
Также на этом этапе обрабатываются ошибки: частичные данные могут быть возвращены даже если некоторые поля не удалось получить.
Собранный ответ передается в Веб-сервер.Транспорт → Клиент
Готовый ответ отправляется клиенту через HTTP.
Таким образом запрос проходит путь от клиента к хранилищам и обратно, и GraphQL-сервер в этом процессе играет ключевую роль.
GraphQL не привязан к конкретному транспортному протоколу. Он может работать через WebSocket, gRPC или др., но чаще всего используется HTTP.
Мы составили общее понимание о работе GraphQL API: запросы приходят от клиента в GraphQL-сервер, он проводит валидацию и вызывает нужные резолверы. Резолверы собирают данные из источников, и GraphQL-сервер собирает их в нужный клиенту ответ.
Но, конечно, API и GraphQL-сервер существуют не в вакууме. И вся эта непростая конструкция должна быть грамотно размещена в архитектурном ландшафте компании. Успех внедрения GraphQL API зависит от того, как его разместить в рамках системы и какие процессы внедрить для его разработки и поддержки.
В зависимости от потребностей бизнеса и зрелости ИТ, GraphQL может выполнять разные архитектурные роли — от частного решения для конкретного клиента, до связующего звена между всеми сервисами.
Разберем подробнее архитектурные роли GraphQL в рамках ИТ-ландшафта компании.
Архитектурные роли GraphQL
GraphQL может по-разному использоваться в архитектуре приложений и всего ИТ-ландшафта компании. Рассмотрим самые распространённые архитектурные паттерны применения этой технологии.
GraphQL с единым источником данных
Самый простой вариант реализации GraphQL — разместить его непосредственно в приложении, которое отвечает за необходимые в API данные:

GraphQL не зависит от типа базы. Ему всё равно, откуда приходят данные:
PostgreSQL, MySQL, MongoDB;
Redis, Elasticsearch;
Файлы, API, даже hardcoded массивы.
Можно обращаться к базе напрямую из резолверов, но обычно GraphQL вызывает соответствующие функции приложения, которые инкапсулируют запросы к базе. Запрос приходит в GraphQL-сервер, резолверы обращаются к функциям приложения, те запрашивают данные в БД, данные возвращаются клиенту.
К плюсам архитектуры с единым источником данных можно отнести:
Быстрый старт;
Простая отладка — все логи в одном месте;
Минимум инфраструктуры — не нужны очереди, шлюзы.
Основным минусом будут сложности в масштабировании:
Всё зависит от производительности одной БД;
Невозможно масштабировать отдельные части системы;
При росте нагрузки придётся переписывать архитектуру.
При таком подходе появляется соблазн создать GraphQL-схему, копирующую структуру БД. Но схема должна отражать потребности клиентов, а не таблицы в базе.
Архитектура, где у GraphQL единый источник данных, хорошо подходит для небольших проектов и MVP. Для больших и средних систем чаще всего GraphQL используют в более сложных конфигурациях. Рассмотрим ниже их концепции.
GraphQL как API Gateway
Ещё один важный сценарий использования GraphQL — это интеграция множества существующих систем за единым GraphQL API.
Для GraphQL не важно, из каких источников получать данные для выполнения запроса. Поэтому его можно использовать, как интеграционный слой, объединяющий данные из разных микросервисов, REST/SOAP/gRPC/… APIs, легаси-систем, внешних сервисов и т.д.
То есть GraphQL-сервер выступает в роли API Gateway:

В этом случае GraphQL-сервер — это отдельное приложение, которое может работать совместно с существующим API Gateway или как отдельный сервис.
Важно, что здесь GraphQL не является частью одной из систем-источников данных. Он используется для объединения и сокрытия сложности существующих систем за удобным GraphQL API.
Такой сервис обладает функциональностью классического API Gateway:
Предоставляет единую точку входа для клиентов;
Скрывает множество backend-сервисов;
Агрегирует данные из разных источников;
Может добавлять авторизацию, кеширование.
То есть новые клиентские приложения могут сразу обращаться к GraphQL-серверу за нужными данными.
К плюсам подхода относятся:
Единый интерфейс для всех клиентов;
Можно постепенно модернизировать backend;
Новые продукты разрабатываются быстрее.
Минусы:
Дополнительная точка отказа;
Может стать узким местом производительности;
Требует синхронизации при изменениях в системах-источниках.
Также в рамках паттерна могут использоваться два разных подхода к организации доступа к данным:
Один GraphQL для всех — веб-, мобильные и другие клиенты используют один и тот же GraphQL endpoint. Схема проектируется универсально. Такой подход использует, например, GitHub.
BFF (Backend for Frontend) — каждый тип клиентов имеет свой GraphQL endpoint с оптимизированной схемой. Пример этого паттерна можно посмотреть здесь.
Часто компании начинают с единого GraphQL и по мере роста эволюционируют в BFF или федерацию, которую мы рассмотрим позже.
Гибридный подход
На практике часто используют комбинацию паттернов с единым источником данных и API Gateway. То есть у GraphQL API есть один основной источник данных, но при необходимости он запрашивает дополнительные данные из других источников:

Типичные сценарии для использования этой архитектуры:
Основные данные из БД + внешние API для специфичных задач;
GraphQL для чтения + REST для загрузки файлов;
Новый функционал через GraphQL + legacy через старые API.
Например, в интернет-магазине товары и пользователи скорее всего будут приходить из основного источника данных, информация о доставке подгружаться из API транспортной компании, об оплате — из API платёжной системы, а поиск осуществляться через Elasticsearch. Всё это будет в единой GraphQL-схеме, но данные будут приходить из разных источников.
К плюсам такой архитектуры можно отнести:
Гибкость и оптимальность для каждого случая;
Можно внедрять постепенно.
Основные минусы скрыты в слове «гибридный»:
Сложнее поддерживать;
Сложнее разобраться новичкам.
Этот подход скорее подойдет средним проектам, в которых:
Есть основной источник данных, но нужны дополнительные;
Не хочется усложнять архитектуру полноценным Gateway;
Происходит переход от монолита к микросервисам.
Однако со временем количество источников растёт, и система может стать трудноуправляемой. В таких случаях важно хорошо документировать интеграционные процессы и вовремя остановиться, чтобы предотвратить хаос. И тут есть два пути эволюции:
Полноценный Gateway — если нужно лучше структурировать интеграции;
Федеративная архитектура — если разные команды хотят владеть своими частями GraphQL
Рассмотрим федерацию — наиболее масштабируемое решение для больших организаций.
Федеративная архитектура
Если за GraphQL появляется слишком много сервисов и источников данных — API Gateway может приобрести все проблемы монолита:
Противоречивые требования от разных сервисов;
Проблемы с координацией;
Перегруз команды в случае, если за GraphQL API отвечает одна команда и конфликты, если граф разрабатывают сразу несколько команд;
Проблемы со стандартизацией.
Поэтому, достигая определённого масштаба, компании часто переходят на BFF или федеративную архитектуру.
В 2019 году Apollo представила Apollo Federation как решение проблем растущих GraphQL API. К 2025 году этот подход стал новым стандартом архитектуры GraphQL на больших проектах. Его используют такие компании как Netflix, Booking и Volvo.
Подграфы
В основе паттерна лежит идея подграфов: вместо одного большого GraphQL-сервера создаётся множество маленьких, и каждый отвечает за свою область. А специальный Gateway (Router) объединяет их в единую схему — суперграф. При этом для клиентов это выглядит как единый GraphQL API — они не знают о разделении на подграфы.
Подграфы должны быть максимально независимыми, с минимумом связей между ними. Обычно их выделяют руководствуясь Domain-Driven Design — каждый подграф отражает отдельную область бизнеса. Также стараются делать так, чтобы одна команда поддерживала один подграф. Иначе возникают всё те же проблемы, что и у единого Gateway.
Например, Netflix разделил единый GraphQL для студийной экосистемы на следующие подграфы:
Movies — всё о фильмах и сериалах;
Production — съёмки, локации, бюджеты;
Talent — актёры, режиссёры, команда.
За каждый подграф отвечает отдельная команда. Но на уровне архитектуры компании эти графы будут объединены в суперграф.
Далее подробнее рассмотрим устройство федерации на примере интернет-магазина:

В нашем примере три подграфа: Clients (клиенты), Products (товары) и Orders (заказы). Каждый подграф отвечает за свою область бизнеса.
В упрощенном варианте схема подграфа Clients может выглядеть так:
type Client {
id: ID!
name: String!
email: String!
registeredAt: DateTime!
}
type Query {
client(id: ID!): Client
clients: [Client!]!
}
В базовой реализации федерации все эти объекты перейдут в суперграф — то есть будут доступны для клиентов.
Как и типы из остальных подграфов. То есть названия у объектов, запросов, мутаций и подписок должны быть уникальными в рамках суперграфа. Иначе Gateway просто не поймет, к какому GraphQL-серверу обращаться за данными для типа.
Для специфичных типов подграфа допустимо использовать префиксы, например ClientStatistics
в подграфе Clients
— это статистика по клиентам, а OrderStatistics
в подграфе Orders
— это статистика по заказам. Если оба подграфа назовут свой тип просто Statistics — возникнет конфликт наименований.
Однако суть Apollo Federation в федеративных типах, которые используются сразу в нескольких подграфах — такие типы обычно описывают общую для всего домена сущность.
Ниже подробнее рассмотрим, как описываются и реализуются такие типы.
Федеративные типы
В федеративной архитектуре, в отличие от обычного Gateway — подграфы могут расширять и использовать типы друг друга. Чтобы сделать тип общим, то есть федеративным — необходимо при его описании добавить директиву @key
и уникальный идентификатор объекта.
В статье про схему в GraphQL мы обсуждали, что директивы служат для разметки различных элементов документа. Они обозначаются знаком @
и указывают валидатору, серверу или клиентским инструментам на необходимость особой обработки этих элементов. В случае федеративных типов директивы предназначены для Gateway, который собирает все подграфы в единый суперграф.
Например, так мы объявим, что другие подграфы могут использовать и расширять объектный тип Client:
//Clients подграф
type Client @key(fields: "id"){
id: ID!
name: String!
email: String!
registeredAt: DateTime!
}
Директива @key
превращает тип Client
в федеративный. Поле в fields
должно однозначно идентифицировать клиента, чтобы по нему Gateway смог собрать весь объект из подграфа Clients. Обычно в fields
указывают ID, но может быть указано и несколько полей — как в простом и составном ключах в базах данных.
После объявления федеративного типа Client
подграф Orders может использовать его в своих типах:
//Orders подграф
type Order {
id: ID!
date: DateTime!
status: OrderStatus!
items: [OrderItem!]!
client: Client! //Используем тип Client из другого подграфа
totalAmount: String!
}
extend type Client @key(fields: "id") {
id: ID! @external
//Объявляем только необходимые поля из подграфа Clients
}
type Query {
order(id: ID!): Order
Внешний тип обязательно должен быть объявлен в подграфе и обозначен как extend type
.
Поля из fields
также должны быть объявлены и помечены директивой @external
. С этой директивой можно объявить и другие поля из подграфа Clients — если их необходимо использовать в резолверах подграфа Orders.
Например, если для расчета totalAmount
с учетом скидок нам необходима дата регистрации клиента — то мы также должны будем объявить поле registeredAt
:
//Orders подграф
extend type Client @key(fields: "id") {
id: ID! @external //Уникальный идентификатор из key
registeredAt: DateTime! @external //Поле, которое используют резолверы Orders
}
Однако каждое внешнее поле требует дополнительного запроса к исходному подграфу, что увеличивает количество сетевых вызовов и латентность. Поэтому чрезмерное использование @external
полей может привести к проблемам производительности.
Директивы @key
и @external
очень важны, потому что по ним Gateway понимает, у какого подграфа какие поля запрашивать.
Так, если клиент вызовет query order
— Gateway при сборе ответа поймет, что поля заказа надо запросить у резолверов подграфа Orders, а поля покупателя — у подграфа Clients:

Для клиента это будет обычный синхронный запрос, но внутри федерации происходит цепочка запросов между Gateway и подграфами:
Gateway получает запрос и определяет, какие подграфы задействованы.
Gateway отправляет запрос в
Orders
подграф, получает данные заказа иID
клиента.С полученным
ID
клиента Gateway делает запрос вClients
подграф.Gateway получает данные клиента из
Clients
подграфа.Объединяет результаты всех подграфов и возвращает единый ответ клиенту.
Если клиент запросил totalAmount
, то на шаге 3 Gateway запросит поле registeredAt
. Следующим шагом будет дополнительный запрос к Orders
для расчета totalAmount с учётом полученной даты регистрации.
То есть добавление одного @external
поля приводит к дополнительному сетевому вызову. В реальных проектах такие цепочки могут стать значительно длиннее, что критически влияет на производительность.
Расширение федеративных типов
Подграфы могут не только использовать федеративные типы, но и расширять их.
Например, подграф Orders
может расширить федеративный тип Client
своими полями:
//Orders подграф
extend type Client @key(fields: "id") {
id: ID! @external
clientOrders: [Order!]! //Расширяем тип Client
totalSpent: Float! //Еще одно дополнительное поле
}
Новые поля из Orders описываются как обычные поля типа. При этом дополнять ими схему подграфа Clients не нужно. Клиент и так их увидит в суперграфе:
//Суперграф
type Client {
//Поля из Clients подграфа
id: ID!
name: String!
email: String!
registeredAt: DateTime!
//Поля из Orders подграфа
clientOrders: [Order!]!
totalSpent: Float!
}
Все подграфы могут расширять федеративные типы, а также использовать все их поля через @external
.
То есть подграф Products может добавить свои поля в тип Client
, а также использовать поля, добавленные подграфом Orsers:
//Products подграф
extend type Client @key(fields: "id") {
id: ID! @external //Из Clients подграфа
clientOrders: [Order!]! @external //Из Orders подграфа
recommendedProducts: [Product!]! //Из Products подграфа
}
Важно учитывать:
Каждый подграф отвечает только за свои поля;
Все подграфы должны использовать одинаковый
@key(fields: "id")
;Gateway автоматически собирает данные из всех подграфов при запросе клиента;
@external
работает со скалярными полями и объектными типами;Интерфейсы и юнионы нельзя пометить как
@key
;Enum'ы должны быть идентичными во всех подграфах, в которых они используются.
Также Apollo Federation позволяет определять одни и те же юнионы и интерфейсы в разных подграфах. Gateway создает единый юнион/интерфейс, содержащий полный набор возможных типов.
Например, для возврата полной информации о товаре в заказе можно использовать информацию сразу из 3х подграфов:
//Orders подграф
union OrderItem = Order
type Order {
id: ID!
total: Float!
}
//Clients подграф
union OrderItem = Client
type Client {
id: ID!
name: String!
}
//Products подграф
union OrderItem = Product
type Product {
id: ID!
name: String!
}
Результирующий юнион в суперграфе будет таким:
//Суперграф
union OrderItem = Order | Client | Product
При запросе Gateway Gateway автоматически объединит все типы из трёх подграфов в юнион OrderItem
.
Федеративные типы позволяют каждому сервису добавлять свою функциональность к общим сущностям, сохраняя архитектурную независимость сервисов при координации через единую схему.
Следующий важный этап при работе с подграфами — это процесс объединения нескольких схем в одну.
Композиция схем
Федерация GraphQL предполагает, что единая схема создаётся из схем подграфов. Этот процесс называется композицией схем (schema composition).
Существует два основных подхода к композиции: статическая и с контролем схемы (managed federation).
Статическая композиция — это процесс объединения схем подграфов в единую схему суперграфа на этапе сборки или запуска Gateway, до обработки клиентских запросов.

Процесс композиции выполняется в Gateway и происходит так:
Сбор схем: Gateway получает SDL всех подграфов (из файлов, переменных окружения или API).
Валидация: Проверяет корректность каждой подграф схемы.
Композиция: Объединяет схемы, резолвит @key директивы, строит граф зависимостей.
Генерация: Создает исполняемую суперграф схему.
Запуск: Gateway готов обрабатывать запросы.
Статическая композиция выполняется вручную или как часть CI/CD-пайплайна. Она не требует подключения к внешним сервисам и подходит командам, которые хотят полностью контролировать инфраструктуру.
Основной недостаток статической композиции заключается в том, что композиция схемы происходит во время запуска Gateway. Таким образом, каждое изменение схемы требует перезапуска Gateway. Это рискованно, потому что если композитная схема не будет валидной, Gateway не запустится и весь суперграф будет недоступен.
У этого способа есть и ряд других ограничений:
При любом изменении схемы в подграфах требуется рестарт Gateway;
Проблемы обнаруживаются только на этапе запуска;
Команды должны синхронизировать релизы;
Сложно протестировать композицию заранее.
Чтобы избежать конфликтов при обновлении схем подграфов придумали федерацию с контролем схемы — Managed Federation.
В managed-подходе схема композируется в Schema Registry и автоматически обновляется при деплое подграфов.
Schema Registry — это концепция отдельного сервиса, созданного специально для композиции схем. Её обычно реализуют с помощью готовых решений. Например, Apollo GraphOS, Hive или Cosmo.
Рассмотрим, как происходит обновление суперграфа при использовании Schema Registry:
Публикация схемы.
Команда разработчиков (не сам подграф) отправляет обновленную схему в Schema Registry через CI/CD или CLI. Обычно это происходит при деплое нового кода подграфа.
Schema Registry выполняет проверки.
Валидация схемы — корректность GraphQL SDL.
Композиция — пытается скомпоновать все подграфы в суперграф.
Breaking changes анализ — сравнивает с предыдущей версией.
Impact analysis — проверяет влияние на существующие запросы.
Результат проверки.
При успехе — создаёт новую версию схемы суперграфа и помечает её как активную.
При ошибках — блокирует публикацию и отправляет отчёт с деталями проблем.
Распространение в Gateway.
Gateway периодически опрашивает Schema Registry на предмет обновлений или подписывается на уведомления о новых версиях.
Gateway обновляет схему суперграфа без рестарта.
Мониторинг.
Schema Registry отслеживает использование полей.
Schema Registry ведёт историю всех изменений схем.
Schema Registry — это централизованная система управления схемами:

Такой подход помогает упростить работу с федерацией, внедрить универсальное решение для поддержки и мониторинга схем. Однако, Managed Federation при использовании готовых решений требует зависимости от внешнего сервиса и может противоречить compliance-требованиям компании. При необходимости полного контроля над данными и процессом публикации схемы компании выбирают статическую композицию, хотя операционно это сложнее.
Возможен гибридный подход: например, разрабатывать и тестировать локально со статической композицией, а затем публиковать схемы в Schema Registry.
Мы разобрали основные принципы федеративной архитектуры на примере Apollo Federation. Но у решения Apollo есть альтернативы, кратко рассмотрим наиболее популярные из них.
Подходы к реализации федеративной архитектуры
На данный момент есть 3 распространённых подхода к реализации федеративной архитектуры:
Federation — директивно-ориентированный подход. Подграфы помечают свои схемы специальными директивами (
@key, @external
), чтобы Gateway понимал, как их связывать. Пример: Apollo Federation, WunderGraph Cosmo.Schema Stitching — программно-ориентированный подход. Gateway программно сшивает разные GraphQL схемы, используя код для связывания типов и полей. Пример: GraphQL Tools, GraphQL Mesh
Гибридные решения — сочетания разных инструментов и функций из Federation и Schema Stitching. Пример: Hasura (Remote Joins), GraphQL Fusion.
Существует паттерн для быстрого объединения нескольких схем в одну, без определения общих типов — API Namespacing Pattern, где сервер при композиции ко всем типам и запросам с одинаковыми названиями автоматически добавляет название подграфа. Таким образом быстро и эффективно исключаются возможности конфликтов в общей схеме. Однако, расширение и переиспользование типов при таком подходе недоступно. Но в ситуациях, когда надо быстро объединить под единый эндпойнт несколько совершенно разных API — namespacing паттерн очень эффективен.
Все эти подходы решают одну задачу — объединение независимых GraphQL сервисов в единый API, но используют разные технические методы.
Выбор подхода зависит от конкретных потребностей:
Federation подходит для организаций с четким разделением доменов и командами, готовыми следовать специфичным соглашениям;
Schema Stitching дает больше контроля над процессом композиции, но требует больше ручной работы;
API Namespacing идеален для быстрой интеграции разнородных API без необходимости их координации;
Гибридные решения позволяют комбинировать преимущества разных подходов.
Федерация сложна и трудозатратна в настройке и поддержке. Она добавляет накладные расходы на производительность (~10-15мс на каждый межподграфный вызов) и создаёт дополнительную точку отказа в виде Gateway.
При всех преимуществах федеративной архитектуры — возможности независимой разработки, чёткого разделения ответственности и масштабируемости — важно помнить, что не все задачи требуют такой сложности. Например, Meta, будучи создателем GraphQL, по-прежнему использует монолитный подход к своему основному API.
Современные решения стремятся упростить разработку и поддержку федеративных систем. Например, компания WunderGraph недавно представила подход к созданию федерации, где вместо подграфов используются gRPC-сервисы. Такая система сохраняет удобство GraphQL для клиентов, но значительно упрощает архитектуру серверной части за счет gRPC. Это делает федеративные решения более производительными и удобными в разработке.
Таким образом федеративная архитектура — это мощный инструмент для масштабирования GraphQL в больших организациях, но не универсальное решение. Успех её внедрения зависит не только от технических аспектов, но и от зрелости процессов разработки, готовности команд к координации и реальной необходимости в независимом развитии частей системы.
Рассмотрим сравнение всех рассмотренных паттернов и ориентировочно наметим варианты использования каждого из них.
Сравнение архитектурных паттернов
Выбор архитектурного подхода зависит от масштаба проекта, размера команды и бизнес-требований. Каждый паттерн имеет свои сильные и слабые стороны. В таблице представлены относительные оценки каждого из паттернов:
Критерий | Единый источник | Гибридный | API Gateway | Федеративная |
Сложность разработки | Очень низкая | Низкая | Средняя | Высокая |
Сложность поддержки | Низкая | Средняя | Высокая | Средняя |
Производительность (latency) | Отличная | Хорошая | Средняя | Средняя |
Пропускная способность | Ограниченная | Средняя | Средняя | Отличная |
Независимость сервисов | Отсутствует | Частичная | Низкая | Высокая |
Точки отказа | БД | БД + Gateway | Gateway | Router + Подграфы |
Координация изменений | Не нужна | Минимальная | Высокая | Автоматическая |
Инфраструктурные затраты | Минимальные | Низкие | Средние | Высокие |
На основе таблицы и всего сказанного можно примерно обозначить варианты использования подходов:
Единый источник данных:
MVP и прототипы;
Небольшие приложения (< 10 разработчиков);
Один основной домен данных;
Быстрый time-to-market.
Гибридный подход:
Расширение существующего приложения;
Основная БД + 2-3 внешних API;
Переходный период к микросервисам;
Средние проекты.
API Gateway:
Интеграция legacy систем;
Много разнородных источников данных;
Нужен единый интерфейс для клиентов;
Enterprise окружение.
Федеративная архитектура:
Крупные проекты (> 50 разработчиков);
Несколько независимых команд;
Четкое разделение доменов;
Высокие требования к масштабируемости.
При этом часто GraphQL API проходит эволюционный путь от единого источника данных до Gateway/Федерации по мере развития проекта.
Типичные траектории развития:
Единый источник → Гибридный: при добавлении внешних API.
Гибридный → API Gateway: при росте количества источников.
API Gateway → Федерация: при росте команды и потребности в автономии.
Каждый переход обычно мотивирован конкретными ограничениями текущего подхода: производительностью, сложностью координации изменений или потребностью в независимой разработке.
Выбор архитектурного паттерна — это всегда компромисс между сложностью, производительностью и гибкостью. Универсального решения не существует, и оптимальный выбор зависит от специфики конкретного проекта и организации.
Заключение
Архитектура GraphQL API представляет собой спектр решений — от простого единого источника данных до сложных федеративных систем. Каждый подход имеет свою область применения и набор компромиссов.
Мы рассмотрели устройство GraphQL-сервера и жизненный цикл запроса — фундаментальные концепции, которые остаются неизменными независимо от выбранной архитектуры; а также четыре основных архитектурных паттерна: единый источник данных, гибридный подход, API Gateway и федеративная архитектура. Каждый решает определенный класс задач и имеет свои ограничения.
GraphQL может работать в различных архитектурных контекстах — от монолитных приложений до распределенных систем. Выбор конкретного подхода зависит от множества факторов: размера команды, сложности домена, требований к производительности и организационной структуры.
Федеративная архитектура решает проблемы масштабирования больших GraphQL API, но требует значительных инвестиций в инфраструктуру и процессы. API Gateway подходит для интеграции существующих систем, но может стать узким местом. Простые подходы эффективны для небольших проектов, но имеют ограничения при росте системы.
Технология продолжает развиваться — появляются альтернативы Apollo Federation, развиваются подходы к управлению схемами, возникают новые способы предоставить API клиенту.
GraphQL, как современный инструмент, подстраивается под задачи и скорость изменений в ИТ-сфере. Однако в этом кроется и его ахиллесова пята — сложный и кропотливый путь по обеспечению безопасности и высокой производительности. Об этом мы поговорим в следующих статьях цикла.
Полезные материалы
Официальные ресурсы
Инструменты для работы с GraphQL
GraphiQL — IDE для интерактивного тестирования запросов
Apollo Studio / GraphOS — облачные инструменты для анализа и отладки схем
Postman и Insomnia — поддержка GraphQL-запросов «из коробки»
Учебники и принципы проектирования
HowToGraphQL — Basics — пошаговый онлайн-курс
Principled GraphQL — практические принципы построения API
Блоги и статьи
GraphQL Architecture: An Introduction — краткий обзор паттернов
GraphQL vs REST — сравнение подходов
Schema-first vs Code-only GraphQL — Apollo Blog
Федерация и масштабирование
WunderGraph Cosmo — альтернатива Apollo Federation с open-source-реестром схем
GraphQL Fusion — экспериментальный подход от The Guild, объединяющий Federation и Schema Stitching
GraphQL Mesh — для интеграции REST/gRPC/SQL/и других API в единый GraphQL слой