Перейти к основному содержимому

ADR-0051: API Gateway (YARP) Как Единая Точка Входа

Статус

Accepted

Контекст

До сих пор frontend pin-admin обращался к двум бэкендам напрямую через dev-прокси Vite с тремя правилами path-based маршрутизации (/api/tenants/.../facilities → pin, /api/(auth|alliances|me|users|tenants) → identity, остальное → pin). Знание о портах и составе сервисов было размазано по фронту (vite.config.ts, .env, axios-instance, auth-interceptor). Схема хрупкая, плохо масштабируется при росте числа сервисов и не даёт единого обзора API.

Решение

Вводим отдельный сервис src/gateway/Pin.Gateway на YARP (reverse proxy) как единую точку входа. Маршрутизация и состав сервисов задаются в appsettings (секция Gateway), без правки кода.

  • Публичная схема URL — /api/{serviceName}/****. Контроллеры downstream-сервисов не меняются (они остаются под /api/...). Gateway вставляет сегмент {serviceName} между /api и остальным путём, а при форвардинге снимает его, сохраняя /api. Модель сервиса в конфиге — две «крутилки» пути: RoutePrefix (публичный сегмент) и DownstreamPathPrefix (реальный префикс на хосте, по умолчанию api).
    • pin, identityDownstreamPathPrefix = api: /api/pin/tenants/1 → pin /api/tenants/1.
    • announcementsDownstreamPathPrefix = api/announcements (самостоятельный сервис announcements-api): passthrough /api/announcements/... → announcements /api/announcements/....
  • Swagger «of swaggers». Gateway отдаёт переписанный OpenAPI каждого сервиса на /api/{serviceName}/swagger/swagger.json (агрегатор тянет исходный документ Swashbuckle и переписывает ключи paths под публичную схему — это нужно, потому что сгенерированные клиенты ValIO берут путь как есть, игнорируя servers). Единый UI — Scalar на /scalar, по документу на сервис.
  • Адрес сервиса зависит от окружения. У каждого сервиса один адрес Gateway:Services:{code}:Url: в appsettings.json — локальный (localhost), в Docker переопределяется переменной окружения Gateway__Services__{code}__Url на внутреннее имя контейнера. CORS для SPA владеет gateway (Gateway:Cors:AllowedOrigins).
  • Catch-all proxy-маршруты получают Order = 100, чтобы gateway-овые эндпоинты (swagger/health) с Order = 0 перехватывали запрос раньше reverse-proxy.

Frontend переводится на единый вход: dev-прокси Vite и связанные конфиги удаляются, SPA ходит в gateway по VITE_API_BASE_URL; источники swagger в valio.config указывают на gateway, после чего клиенты регенерируются с URL вида /api/{serviceName}/....

Последствия

  • Один сетевой адрес для фронта; добавление сервиса = запись в Gateway:Services, без правок кода.
  • Требуется регенерация клиентов ValIO (npm run generate:api при поднятом gateway) — до неё SPA обращается по старым путям /api/..., не совпадающим с маршрутами gateway.
  • Аутентификация/авторизация на gateway пока не вводится (passthrough заголовков, в т. ч. Authorization); централизация JWT на gateway — отдельное будущее решение. См. ADR-0025.

Связанные документы