Контракт участника¶
NATS JetStream — основная шина. Вокруг неё любой зоопарк участников, при одном условии: участник соблюдает этот контракт. Контракт — единственная связанность в системе; всё остальное независимо и сменно (event-bus-concept.md, разделы 1, 7).
Это не просто AsyncAPI-доки — это runtime-правила, которые делают участника полноценным членом шины. Шаблоны в ../templates/ их запекают, так что соблюдать дешевле, чем нарушать.
Что обязан участник¶
-
Идентичность и доступ. Свои креды NATS (
user/passwordили nkey) с allow-list ровно на свои subject'ы — публиковать/читать только заявленное в контракте (docs/external-access.md,docs/secrets.md). Контракт = ворота на уровне рантайма. -
Именование. Subject'ы по naming.md:
сущность.событие.vN, событие в прошедшем времени (факт, не команда), версия в subject-токене. Никакого PII в subject'е. -
Контракт-первым + валидация. Каждый публикуемый subject описан в ../contracts/asyncapi.yaml и имеет JSON Schema в ../contracts/schemas/. Участник валидирует исходящее сообщение против схемы перед публикацией — ответственность за корректность на источнике. Subject без контракта не существует.
-
Correlation. Читает
traceparent(W3C) из входящего сообщения и пробрасывает его на производные факты; на границе системы генерит, если его нет (correlation-id.md). Обрыв ID — дефект. -
Durability. Durable JetStream-консьюмер с именем
<участник>-<назначение>(идентичность, lifecycle — раздел 9). Ack только после успешной обработки; обработка идемпотентна (дедуп по event-id /Nats-Msg-Id). При выводе участника консьюмер удаляется явно (иначе осиротевший pending). -
Только контрактная связанность. Участник реагирует на subject'ы и публикует факты. Он никогда не зовёт другого участника напрямую — нет RPC между участниками, только шина. Это и есть слабая связанность.
Сериализация — JSON (event-bus-concept.md, раздел 7).
Два класса участников¶
-
Прямые (NATS-нативные: Go/TS/Python с NATS-клиентом) — говорят с шиной сами. Durability берётся из JetStream-консьюмера (ack + идемпотентность). Им не нужны «4 правила моста» — те про стык с не-NATS-движком. Шаблоны
templates/participant-*— под этот класс. -
Движки за адаптером (Inngest, Windmill, …) — не говорят NATS (или не должны). Их подключает тонкий мост: вход — подписчик шины → API движка, выход — результат функции в шину. Тут и живут 4 правила надёжности (../stacks/bus/bridge/README.md, раздел 5). Выход переиспользует движок-агностичный
bridge /publish.
Внешний компьют (напр. Modal.com) — это бэкенд участника, не член шины: Python-участник гонит тяжёлое в Modal и публикует результат фактом обратно.
Жизненный цикл (раздел 9)¶
- Добавление: контракт раньше кода (PR в AsyncAPI + схема) → завести креды (Infisical) + allow-list → поднять участника со своим консьюмером → отразить в каталоге.
- Изменение: совместимое — свободно; ломающее — только через версию в
subject (
.vN) и параллельный период (#4), никогда на месте. - Удаление: проверить зависимости (каталог) → вывести → явно удалить консьюмер → отозвать креды → PR в контракты.
Соответствие проверяется¶
Шаблоны делают соблюдение дешёвым; ворота делают нарушения видимыми: валидация схем в CI, каталог зависимостей (#3), версионирование (#4). Без них зоопарк дрейфует.