gitaspen docs

BMGP — Base Multi Gateway Platform

Шлюз — реверс-прокси под конкретный фронт. Собирает в одно API ручки из нескольких бэков (BMBP), нужных этому фронту. Фронт ходит только в свой шлюз и не знает, сколько за ним бэков и какие они. Состав шлюза определяется со стороны фронта («какие ручки мне нужны»), не со стороны бэка («как я сгруппирован»). Один фронт — один шлюз.

5 минут

Шлюз — реверс-прокси под конкретный фронт. Собирает в одно API ручки из нескольких бэков (BMBP), нужных этому фронту. Фронт ходит только в свой шлюз и не знает, сколько за ним бэков и какие они. Состав шлюза определяется со стороны фронта («какие ручки мне нужны»), не со стороны бэка («как я сгруппирован»). Один фронт — один шлюз.

Слой опциональный: если фронт работает с одним бэком, шлюз не нужен.

Структура шлюза

Шлюз — папка с конфигурацией прокси и обвязкой запуска. Прикладного кода нет.

Код
<frontend>_gateway/
├── compose                          # запуск контейнера прокси
├── proxy/
│   ├── container                    # образ
│   ├── proxy.conf                   # общие настройки: лимиты, кеш, заголовки
│   └── conf.d/
│       ├── api.conf                 # маршруты: публичный URL → внутренний URL бэка
│       ├── upstreams.template       # список бэков с плейсхолдерами адресов
│       └── cors.conf                # CORS-заголовки
├── tools/
│   ├── entrypoint                   # старт: подставить адреса в upstreams → собрать доки → запустить прокси
│   └── generate-openapi             # собрать openapi.json из api.conf
├── docs/
│   ├── openapi.json                 # сгенерированная спека публичного API
│   └── ui/                          # Swagger / Redoc / Scalar — раздаётся на /docs
├── .secrets/                        # переменные окружения (адреса бэков), вне VCS
└── README

Три файла, которые правят при работе:

  • api.conf — какие публичные URL и куда проксируются;
  • upstreams.template — список бэков;
  • proxy.conf — общие настройки.

Связь файлов в рантайме

Наружу опубликован только шлюз — один публичный порт. Бэки шлюз зовёт по внутренним адресам из upstreams. Какой именно бэк — определяется маршрутом.

Код
фронт → шлюз :<порт> → один из бэков (BMBP) → api → core → … → БД

Порядок старта (entrypoint):

  1. Подставить адреса бэков из .env в upstreams.templateupstreams.conf.
  2. Сгенерировать openapi.json из api.conf.
  3. Запустить прокси.

Как именно сервисы изолированы от прямого доступа (приватная сеть, файрвол, внутренний bind) — обвязка развёртывания, не часть архитектуры. Архитектурно важно одно: фронт ходит через шлюз, сервисы наружу не опубликованы.

Маршруты

Каждый публичный маршрут = один блок в api.conf, проксирующий на внутренний путь операции одного из бэков (из external/front или external/back BMBP). Разные маршруты идут в разные бэки — это и есть агрегация:

Код
/api/orders/{id}    →  orders_service /api/front/orders/{id}
/api/users/me       →  auth_service   /users/me
/api/files/upload   →  files_service  /files/upload

Адреса orders_service, auth_service, files_service — из upstreams.conf, значения — из переменных окружения.

Для фронта это одно цельное API под одним адресом — без понимания, что за каждой ручкой стоит свой бэк. Публичные пути не повторяют внутренние; снаружи не видно ни состава бэков, ни их группировки. Что не описано маршрутом — наружу не идёт (хвостовой location / отвечает 404).

Два смысла слова «шлюз»

Слово применяется к двум разным вещам, и их различают суффиксом в имени:

Что этоИмяРоль
реверс-прокси перед бэками (этот документ)<продукт>_api_gatewayсобирает API для одного фронта
самостоятельный фронт-продукт<продукт>_gatewayсайт или приложение, с которым работает человек

Правило простое: _api_ в имени означает прокси без прикладного кода. Фронт-шлюз обращается к своему API-шлюзу, а не к бэкам напрямую.

Настройка upstream

У каждого бэка — свой блок upstream. Кроме адреса задаются параметры устойчивости и переиспользования соединений:

nginx
upstream orders_service {
    server ${ORDERS_SERVICE_ADDRESS} max_fails=3 fail_timeout=30s;
    keepalive 32;
}
  • max_fails и fail_timeout — после скольких неудач подряд и на какой срок бэк считается недоступным. Без них каждый запрос продолжает ждать таймаута упавшего сервиса.
  • keepalive — сколько соединений держать открытыми. Размер выбирается по нагрузке: для частых коротких запросов больше, для редких тяжёлых меньше. Ноль означает новое TCP-соединение (и новое рукопожатие) на каждый запрос.

Адрес берётся из переменной окружения, а не пишется в конфигурацию: перенос бэка на другой сервер меняет только окружение шлюза.

Служебная ручка /health отвечает на самом шлюзе, без обращения к бэкам — по ней проверяется, что жив он сам, а не вся система.

Сквозное

Задаётся один раз в proxy.conf / cors.conf, применяется к маршрутам:

  • Лимиты запросов — зонами под тип нагрузки: строже для входа/auth, мягче для read-only.
  • CORS — целиком на шлюзе, приложение про CORS не знает.
  • Кеш read-only — короткий, ключ с учётом Authorization.
  • Авторизация — шлюз пробрасывает Authorization без изменений; токен проверяет сам сервис.

Инварианты

  1. Фронт знает только адрес своего шлюза, не адреса бэков.
  2. Состав шлюза определяется со стороны фронта; бэки про шлюзы не знают.
  3. Один фронт = один шлюз; новый фронт — новая папка, существующие не трогаем.
  4. Каждый внешний маршрут объявлен явно; неизвестный путь — 404.
  5. Шлюз несёт только срез операций своего фронта.
  6. Лимиты, CORS, кеш — на шлюзе, не в сервисе.
  7. Шлюз маршрутизирует, не переформатирует ответы — конверт { status, data } не меняется.

Добавление шлюза

Начинаем со стороны фронта: какие операции ему нужны и из каких бэков они приходят.

  1. Скопировать папку существующего шлюза как <frontend>_gateway/.
  2. В upstreams.template перечислить все бэки, из которых фронт берёт ручки.
  3. В api.conf на каждую нужную фронту операцию завести маршрут: публичный URL → внутренний путь соответствующего бэка; подключить зону лимита, при чтении — кеш.
  4. В .secrets/ заполнить адреса бэков.
  5. В compose указать порт и среду.
  6. Поднять; openapi.json соберётся на старте, доки доступны на /docs.

Наш стек

nginx, Docker, docker-compose. envsubst подставляет адреса бэков в upstreams.template; скрипт на Python собирает openapi.json из api.conf. На другом прокси роли те же — карта маршрутов, список адресов, общие настройки.

Инструкция не помогла?

Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.