null

API gateway все-таки нужен

Предыстория

Несколько месяцев назад в очередном "кофейном" разговоре с коллегами зашло обсуждение необходимости внедрения API gateway в инфракструктуру серверных приложений. Касательно деталей, мы обсуждали реализацию такого служебного сервиса в экосистеме Java/Kotlin Spring. В тот момент я не нашел аргументов в пользу необходимости тратить драгоценные ресурсы (прежде всего время разработки) на внедрение и дальнейшую поддержку gateway. Спустя эти несколько месяцев у меня прибавилось опыта и... вот они запоздалые аргументы.

Немного матчасти

Понятие API gateway применимо прежде всего к микросервисной архитектуре. Причем при традиционном подходе к рассмотрению вопроса предполагается, что в рассматриваемой системе функционируют десятки этих самых микросервисов. Доменная модель разрезана на более менее изолированные друг от друга контексты, по этой причине данные оказываются размазаны между несколькими базами данных. Причем сделаем оговорку, что мы рассматриваем случай "по учебнику" - по этому каждый микросервис использует свою собственную базу данных, как минимум логически изолированную от БД остальных сервисов. API gateway выступает в роли служебного сервиса, к которому обращаются клиенты, а уже API gateway ходит в API микросервисов.

The API-Gateway architectural pattern | Download Scientific Diagram

Простор для дискуссий

На картинке выглядит красиво, "правильно", даже интуиция голосует "за". Однако когда дело доходит до реализации сразу всплывают подводные камни, самый большой из которых - деньги. Даже если рассматривать вариант с использованием фреймворков для построения API gateway таких, как, например, Spring Cloud API Gateway, неизбежно потребуется тратить время (следовательно деньги) на поддержание конфигурации API gateway в актуальном состоянии, не говоря уже о том, что по-хорошему gateway нужно функционально, регрессионно, интеграционно тестировать - ведь поломать фичи можно даже правя конфиг.

Особенно остро вопрос целесообразности реализации API Gateway встает в небольших системах, состоящих из двух-трех сервисов. Клиентам трудно ходить по трем различным доменам? Сделаем nginx-проксю на одном домене. Нужно агрегировать данные с двух и более микросервисов? Черт с ним, сходим по HTTP с одного микросервиса на другой. Другие доводы также легко можно парировать "костылями".

Однако автор вынужден согласиться с тем, что при небольших размерах проектирумой системы и при ограниченных ресурсах на разработку, городить API Gateway - точно провальная мысль. Сорвутся сроки, не хватит бюджета == fail. Но все же остается открытым вопрос: а надо ли тогда в таких условиях писать несколько сервисов (микросервисов) или можно обойтись монолитом? Будет ли решение на основе монолита плохим?

"Четкие" доводы в пользу API Gateway

Закончив с открытой дискуссией, перейдем к формулировке более-менее четких "за" в порядке их важности. Почему все-таки API Gateway нужен?

  1. Агрегация данных. Пожалуй, самая больная тема для разработчиков в случае пускай и не микросервисной, но сервисной архитектуры. На основе практического опыта могу сформулировать "закон джунглей": если данные разделены между несколькими сервисами, их НУЖНО будет агрегировать, причем МНОГО агрегировать. Можно, конечно, свалить агрегацию данных на разработчиков frontend части системы, но п о к а к о й - т о неизвестной причине "фронты" очень не любят этим промышлять. Кроме того, множество запросов к различным сервисам ради одной функции негативно скажется на пользовательском опыте - больше запросов, больше задержка.
    (P.S. Spring Cloud API Gateway не поддерживает агрегацию данных, разработчики считают это антипаттерном, о чем открыто заявили здесь.)
  2. Распределенные транзакции. Еще один "закон джунглей": если данные разделены между несколькими сервисами, их НУЖНО будет модифицировать атомарно, причем МНОГО модифицировать. Когда под рукой есть API Gateway, всегда есть вариант реализовать велосипед распределенные транзакции на основе программного two-phases-commit, руководствуясь принципом "согласованность в конечном счете". Да, мы лишаемся атомарности транзакции как таковой и возникают вопросы о durability, но интуиция подсказывает, что и с этим можно что-нибудь придумать.
  3. Безопасность. Данный пункт на самом деле состоит из двух подпунктов. Во-первых, если клиенты не ходят напрямую в сервисы, возможно реализовать проверку подлинности пользовательского запроса в одном месте, на API Gateway, и не делать ее на самих сервисах. Более того, разграничение ролей можно также выставить на API Gateway, что, очевидно, уменьшает сложность написания микросервисов. Также в пользу "аутентификации на API Gateway" - если вы используете JWT (или на самом деле любые другие токены, проверка подлинности которых ресурсозатратна), можете поймать просадки по перфомансу, если будете проверять токен на каждом микросервисе. И, во-вторых, старый добрый HTTPS, который между прочим шифрует каждый запрос и каждый ответ - если микросервисы доступны только внутри приватной сети, может быть вполне себе рабочим вариантом построить коммуникацию API Gateway <-> Microservice на "голом" HTTP. Не стоит забывать, что шифрование в HTTPS даже более затратно, чем проверка подлинности JWT.
  4. Версионирование API и схемы данных. С использованием API Gateway становится возможным гибко перенаправлять запросы в новые версии эндпоинтов микросервисов, причем совершенно незаметно для клиентов. Кроме того, gateway может менять схемы возвращаемых данных, что дает еще большую гибкость в вопросах спецификации API для клиентов.
  5. Распределенное логирование. Хотя это уже существенно более слабый аргумент в пользу API Gateway, но все, очевидно, на одной точке входа можно залогировать входящий в систему запрос, исходящий ответ, а также снабдить запросы между микросервисами идентификатором, логировать ошибки, пойманные клиентам и т.д.
  6. Кеширование. На API Gateway также, очевидно, можно обмазаться "программными" кешами весьма гибко. Например, если для фичи X нужно агрегировать данные с микросервисов A и B, причем известно, что ответ сервиса A изменяется редко, можно ответ от A положить в кеш и реально сходить только в микросервис B.

Итого

Персональное, личное, субъективное мнение автора таково: если уж использовать архитектуру на основе сервисов (микросервисов), то нужно делать API Gateway.