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,identity→DownstreamPathPrefix = api:/api/pin/tenants/1→ pin/api/tenants/1.announcements→DownstreamPathPrefix = 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.
Связанные документы
- docs/06-services/gateway/README.md
- ADR-0001: Monorepo и границы импортов
- ADR-0028: API Versioning и Client Generation
- ADR-0030: Service Bootstrap, Health Checks и CI Gates