
Реализация системы с микросервисной архитектурой редко обходится без классического разруливающего REST‑гейтвея. Но когда ваша система растёт годами, а в гейтвее плодятся сотни ручек с просачивающейся бизнес‑логикой, можно внезапно обнаружить, что ваш REST‑гейтвей стал монолитом со всеми вытекающими последствиями.
Мы в Авто.ру шли к этому состоянию гейтвея довольно долго. История его началась в 2015 году: десятки разработчиков, сотни ручек, почти 300 000 строк кода — и релизы, которые можно катить неделю. Чтобы спасти наш стремительно деградирующий time‑to‑market и вернуть разработке гибкость, мы решили попробовать GraphQL‑федерацию. Спойлер: кажется, получилось.
Меня зовут Кирилл Ершов, я бэкенд‑разработчик в Авто.ру, и в этой статье я расскажу, как мы перешли от REST к федерации GraphQL: зачем нам это понадобилось, с какими подводными камнями мы столкнулись, как выглядели первые миграции трафика, к чему всё это привело на данный момент в цифрах и инфраструктуре.
Эта статья — адаптация моего доклада со Scala‑митапа 2025. Если вам удобнее видеоформат, то можете посмотреть запись выступления.
Как мы пришли к GraphQL и зачем он нам понадобился
Пара слов о том, что такое GraphQL. Его часто путают с кучей не связанных с ним вещей: SQL, графовыми базами данных или даже языком программирования. На самом деле всё проще: GraphQL — это язык запросов, который клиент использует, чтобы получить данные от сервера.
Если сравнивать с привычным REST, разница становится очевидной. В REST у нас есть endpoints, заточенные под конкретные сущности и бизнес‑логику, — например, GET /traffic-lights
вернёт список светофоров, где у каждого будут ID, цвет и действие.
{
"id": 1,
"color": "RED",
"action": "STOP"
},
{
"id": 2,
"color": "YELLOW",
"action": "WAIT"
},
{
"id": 3,
"color": "GREEN",
"action": "GO"
}
В GraphQL запрос выглядит иначе: мы явно указываем, какие поля хотим получить. Вам нужны только Color
и Action
?
trafficLights {
color
action
}
Отлично — в ответе будет только это. ID не просили — его и не пришлют. В двух словах: клиент формирует структуру ответа сам, под свои нужды, опираясь на единую схему данных.
{
"data": {
"trafficLights": [
{"color": "RED", "action": "STOP"},
{"color": "YELLOW", "action": "WAIT"},
{"color": "GREEN", "action": "GO"}
]
}
}
Как было раньше
Вернёмся в недалёкое прошлое. Вот так выглядела архитектура Авто.ру раньше:

Довольно типичное веб‑приложение с микросервисной архитектурой. На его верхушке — REST‑гейтвей autoru-api
, который знает обо всех ручках, выполняет авторизацию, читает хедеры и проксирует запросы к бэкендам и их системам хранения.
К концу 2023 года цифры нашего гейтвея заставили нас задуматься:
более 40 разработчиков,
из восьми команд регулярно контрибутят в
autoru-api
,за 2023 год он вырос на 50 000 строк кода,
а к концу года достиг 290 000 строк кода.
Короче говоря, наш гейтвей давно перестал быть «просто проксирующим компонентом» — в нём осела куча бизнес‑логики.
Можно долго рассуждать на тему «почему так вышло». История Авто.ру довольно длинная, а с 2015–2016 годов многое изменилось: и технологии, и паттерны проектирования, и команда заметно выросла. В какой‑то момент наша микросервисная архитектура окончательно растеряла свои преимущества грануляции и разделения доменов: стало слишком удобно «сходить» сразу в несколько бэкендов, скомпоновать ответы и приправить всё это бизнес‑логикой прямо внутри гейтвея. В результате мы получили ситуацию, когда почти любой неудачный дифф одной команды может заблокировать релизы всех остальных, а релиз в прод длиной в неделю стал нормой.
В какой‑то момент мы честно признались себе в том, что наш любимый autoru-api
в том виде, в котором он существует, нам больше не подходит.
Новый гейтвей и федерация GraphQL
Решение оказалось неординарным: мы решили развернуть GraphQL‑федерацию. Чтобы понять, что это такое, давайте вспомним определение федерации как формы государства. Федерация — это форма государственного устройства, представляющая собой объединение отдельных субъектов федерации в целостную структуру.
Так и наши подграфы — относительно самостоятельные единицы: могут работать и не в составе федерации, а как самостоятельные бэкенды, выдавая только сущности своего домена. У подграфа есть «карта территории» — подсхема. Все подсхемы собираются в одну большую суперсхему — «карту» федерации. Вот глядя в эту «карту», роутер и понимает, куда и как ему надо добраться, чтобы вернуть запрошенные поля ответа.
Наша GraphQL‑федерация выглядит примерно так.

В основе всё ещё наши привычные микросервисные бэкенды на Scala — они общаются по gRPC или HTTP. Над ними мы добавили ещё один слой — GraphQL‑бэкенды‑прокси. Они тоже написаны на Scala, но уже с использованием Caliban — это функциональная библиотека, реализующая GraphQL‑движок для Scala.
Каждый из таких подграфов описывает все свои сущности, за выдачу которых отвечает, и хранит их в своей подсхеме. Эти подсхемы потом собирают специальными инструментами в суперсхему — глобальную схему всего Авто.ру. В ней описаны все сущности сервиса и связи между ними.
Этой суперсхемой пользуется Apollo Router — наш принципиально новый компонент, который и делает всю эту «GraphQL‑магию». Его придумала компания (внезапно) Apollo, и написан он на Rust — быстрый и нетребовательный к памяти. Роутер разбивает входящий запрос на части, анализирует, составляет план выполнения, запрашивает данные из нужных подграфов, склеивает результат и возвращает клиенту уже собранный ответ.
Новый гейтвей, в свою очередь, теперь предельно простой:
у него только один endpoint: /graphql,
он собирает http‑хедеры и авторизует запрос,
и прокидывает его дальше в Apollo Router.
Допустим, клиент хочет получить ленту Бортжурнала с информацией об авторе каждой записи. Гейтвей принимает GraphQL‑запрос, проверяет авторизацию и передаёт запрос в роутер. Тот смотрит в суперсхему и видит, что данные нужны из двух подграфов — Бортжурнала и Паспорта пользователей. Роутер ходит в оба подграфа, собирает нужные данные и возвращает клиенту единый ответ.

Почему наш GraphQL не совсем каноничный
Сейчас мы не можем запрашивать у исходных бэкендов ровно те поля, которые нужны клиенту. Вместо этого мы забираем сущность целиком, а потом на уровне GraphQL‑слоя фильтруем и возвращаем только нужные поля. По сути, полную гибкость GraphQL мы раскрыть не можем: часть «магии» теряется.
Так вышло по ряду причин:
Во‑первых, потому что у нас уже есть большая зрелая инфраструктура, которая давно работает в проде. Перевести её на новую архитектуру — это как чинить самолёт прямо в полёте. Наша цель была не создать идеальную «Pure GraphQL‑федерацию» с нуля, а мягко мигрировать, не ломая пользователям сервис.
Во‑вторых, GraphQL лучше всего работает, если ваши данные хранятся в максимально «плоском» виде: каждое поле сущности — отдельный столбец в базе. Но у нас не всё так просто: например, в нашей команде часто используется паттерн хранения, когда ключевые поля выносятся в отдельные столбцы (для индексов и фильтрации), а сама сущность хранится в JSON‑столбце. Нам всё равно придётся вытащить всю сущность целиком.
Если создавать новый домен в составе Авто.ру и с самого начала писать его под GraphQL, можно попробовать раскрыть технологию на всю мощь. Правда, как минимум, придётся столкнуться с проблемами разграничения доступов разных клиентов до разных полей сущности…
Ну и как он там, этот ваш GraphQL?
Благодаря этому подходу мы серьёзно сократили радиус влияния каждой отдельной команды. Теперь разработчики могут контрибьютить код только в рамках своего домена и не трогают общие компоненты вроде гейтвея или роутера.
Что это дало на практике:
Писать бизнес‑логику вне своего домена стало невозможно, ну или настолько сложно, что этим никто не хочет заниматься.
Гейтвей почти не собираем и не катим — директивы авторизации и сбора данных по реквесту меняются очень редко, а все доменные изменения уезжают через сборку суперсхемы (и роутера).
Time‑to‑market разработки реально ускорился — сейчас надо постараться, чтобы сломать общий компонент и заблокировать коллег.
На сегодняшний день мы успели перевести на GraphQL примерно 100 RPS из 10 000. Да, путь только начинается, но мы чётко видим, куда идти дальше.
Останавливаться на достигнутом мы, конечно, не собираемся. Это был скорее пилот: на нашей функциональности — то есть внутри команды UGC‑сервисов — мы провели все пусконаладочные работы, ресёрч и запуск в проде, чтобы проверить, как GraphQL живёт под реальной нагрузкой. Сейчас наши основные генераторы трафика — карточки и листинг Офферов, Каталог, Поисĸовая выдача — находятся в активной фазе переезда. Так что скоро к текущим 100 RPS можно будет смело приписать два нуля справа — и это только начало.
Есть и косвенные эффекты: за 2024 год autoru-api
вырос всего на 18 000 строк кода. Сложно утверждать, что это исключительно заслуга переезда на GraphQL, но связь всё‑таки есть: часть логики и новых фич теперь живёт в подграфах, а не в гейтвее.
А что по производительности? Вот пример: на графике видно 95-й перцентиль времени ответа ручки PersonalizedFeed
(персональная выдача Бортжурнала). Жёлтая линия — это GraphQL, зелёная — старый autoru-api
. Видно, что мы слегка замедлились, но тайминги стали заметно ровнее и стабильнее — нам это подходит. Чтобы честно сказать, ускорились мы «на круг» или нет, нужно перевести на новую архитектуру хотя бы пару‑тройку тысяч RPS, и тогда можно будет сравнить объективно.

С какими сложностями мы столкнулись на пути
Разумеется, не всё было гладко. Apollo Router сам по себе не сделал «вжух» и не решил все проблемы. Вот пара забавных (и поучительных) сложностей, с которыми мы столкнулись:
Концепция GraphQL сильно отличается от того, к чему мы привыкли — к gRPC или HTTP‑сервису поверх protobuf‑моделей. Идея суперсхемы, где все знают обо всех, сильно расходится с принципом инкапсуляции деталей, и у многих коллег (и у меня самого в первую очередь) поначалу это вызывало сильный диссонанс.
Старый
autoru-api
всё равно никуда не исчезнет. Во‑первых, переезд займёт ещё много времени. Во‑вторых, у нас есть B2B‑продукты и внешний API для партнёров, где GraphQL использовать не планируем, — поэтому REST останется с нами надолго.Пришлось нам даже завести конвенцию именования сущностей. Почему? Потому что в суперсхеме собраны все сущности, и вполне нормально, что в рамках всего Авто.ру существует несколько
record
илиcount
в разных доменах. Мы решили эту коллизию просто: добавляем префикс домена —logbookRecord
,commentCount
и так далее.Ещё одна забавная история случилась уже сразу после первого запуска в проде. Мы были очень довольны: федерация работает, Apollo Router летает, всё красиво. И почти сразу заметили по трейсам, что наш gRPC‑бэкенд по старой привычке продолжает ходить вбок к соседним сервисам — подтягивать данные о пользователях, лайках, комментариях. В итоге вся эта информация отдавалась федерации, а роутер всё это выкидывал и снова шёл за теми же данными сам в подграфы. Пришлось экстренно чинить это прямо в бою.
Есть и нюансы у системы ошибок GraphQL. Она всегда старается вернуть клиенту максимум данных, даже если что‑то пошло не так. Часть ошибок передаётся отдельным текстовым полем в теле ответа. Получается как в меме:

Инфраструктура тоже потребовала от нас много работы. Логи, метрики, трейсинг, алерты — всё это приходилось либо поднимать с нуля, либо серьёзно переделывать под новую архитектуру. Apollo Router для нас — это чёрный ящик: он написан на Rust, и дорабатывать его сложно. В Scala‑комьюнити, откровенно говоря, энтузиастов писать Rust пока немного, так что живём по принципу «бери что дают».
Отдельно стоит упомянуть безопасность. Безопасность в GraphQL уже давно обмусолена в интернетах. Сами для себя мы решили этот вопрос следующим образом: создали и настроили систему whitelist‑запросов (потратив на это десятки человеко‑часов) и допускаем GraphQL‑запросы только от доверенных BFF наших клиентов (web и apps).
Нагрузочное тестирование
Ещё одна забавная история про то, как мы перед запуском искали ответ на вопрос: «А сколько железок‑то выделить?» Подготовив запросы, я отправился в наши яндексовые нагрузочные стенды, чтобы проверить предел нагрузки, и получил примерно такие результаты:

* 1 core ~ 2000 MHz
Что удивило — первый замер начал сыпаться уже примерно на 100 RPS. Я поинтересовался у коллег — адоптеров GraphQL: «Ребята, ЧЯДНТ? Ваш Rust хвалёный на 100 RPS разваливается». В итоге выяснилось, что я формировал запросы неправильно — вставлял значение переменной прямо в тело запроса, а надо было в поле variables. Получилось, что каждый запрос в роутер не попадал в кеш и был «новым», то есть для него заново делались все операции вроде разбора, анализа, построения плана запроса и т. д.
Быстренько переделав запрос и сложив переменные куда надо, я увидел как раз то, что и ждал:

Для последнего замера мне так и не удалось нащупать точку разлада: сетап отвалился на 14 000 RPS, но не по своей вине, а из‑за инфраструктурных механизмов безопасности. Обходить их для замеров было долго, а самое главное, не особо нужно: всё, что хотел, я уже понял. Такой сетап мы и выбрали для релиза:
CPU: 6 cores (12 GHz)
Memory: 1 GB
Кстати, даже под пиковой нагрузкой в любом сетапе роутер не потреблял более 300 MB.
Ощущения
Цифры цифрами, а важнее то, что мы реально чувствуем в работе. По личным ощущениям жить стало проще и лучше. Раньше любой тикет, затрагивающий релиз монолитного autoru-api
, делался неделю с учётом сбора подтверждений, отладок и тестов (а также их починки). В тех местах, где федерация внедрена, мы уже об этом забыли — пишем код в рамках своих доменов и почти не трогаем гейтвей. Time‑to‑market сократился ощутимо: сколько именно процентов, ещё посчитаем по тикетам и Story Points, но даже без метрик — мы работаем реально быстрее.
Конечно, идеального «везде и сразу» и не получилось. Мы ещё развиваем инструменты наблюдаемости, пишем документацию, шлифуем CI‑проверки и процессы. Но главное — мы получили для себя Proof of Concept в отношении федерации GraphQL. Мы убедились, что такая архитектура закрывает потребности нашей разработки в Авто.ру, а значит, будем расширять эту историю дальше. У нас впереди переезд крупных генераторов трафика — Офферов, Каталогов, Поисковой выдачи — и ещё тысячи RPS, которые нам предстоит перевести на новые рельсы.
Спасибо, что дочитали. Если интересно, как мы будем развивать федерацию дальше, оставайтесь с нами и следите за нашими статьями. А если есть вопросы — пишите, с удовольствием отвечу.