Перейти к содержанию

Explanation: архитектура и принципы

Зачем система устроена именно так. Это разбор идей, не инструкция и не справочник. Полный текст — event-bus-concept.md в корне; здесь — суть и границы.

Два слоя, граница по событию

  • Шина (NATS JetStream) отвечает на «что произошло»: факты, межсистемная связь, персистентность. Хребет.
  • Движок (Inngest и др.) отвечает на «что с этим сделать, надёжно и по шагам»: оркестрация, состояние, ретраи.

Событие живёт в шине. Его обработка живёт в движке. Граница проходит по событию.

Движок — участник шины через тонкий мост, а не центр. Нейтральная точка — всегда шина; движок сменяем, не трогая контракт.

Три плана, не свалка в одном стеке

Топология разведена по сетям, и это не косметика — это та же граница, что и в концепте, выраженная в деплое:

  • Шина (event-bus) — NATS. Сюда подключаются разнородные участники (stacks/integrations) и мост.
  • План движка (engine) — connect-gateway Inngest и OUT-эндпойнт моста (/publish). Сюда смотрят durable-воркеры (stacks/functions). NATS тут нет.
  • Приватное (internal) — хранилища движка (postgres/redis), наружу не светятся (ADR 0001).

Ключевой вывод: воркер-функция — это «рука движка», а не сервис на шине. Её вызывает движок (connect), результат она отдаёт только через мост, NATS она не трогает. Поэтому воркеры живут своим стеком на плане engine, а stacks/bus остаётся чистой инфраструктурой — новый воркфлоу не правит инфра-стек. Раньше воркеры лежали внутри bus лишь потому, что у движка не было разделяемого плана; сеть engine это чинит, не нарушая «Inngest не на шине» (events через границу — по-прежнему только мостом; воркеру даже не выдаётся event-key).

Контракт — единственная связанность

Участники разнородны (разные языки, стеки, внешние). Их объединяет только контракт: subject'ы, схемы, correlation, durability-конвенции. Никаких прямых вызовов между участниками — реакция на факты. Это и даёт расширяемость: добавление/удаление участника — рутина (см. how-to), потому что ничего, кроме контракта, не связывает.

Чтобы это держалось, контракт — ворота: валидация схем и каталог в CI (#3), версионирование при ломающих изменениях (#4). Шаблоны делают соблюдение дешёвым; ворота делают нарушения видимыми.

Рантайм-агностичность

Шине всё равно, на чём участник — Go/TS/Python, декларативный Benthos, движок Inngest/Windmill, managed-compute Modal. Выбор инструмента — по потребности, а не по моде (ADR 0002). Внутреннее состояние участника приватно (ADR 0001).

Надёжность и наблюдаемость

At-least-once шины + идемпотентность участников = безопасные повторы. Мост держит 4 правила (детерминированный id, ack-после-2xx, бэкпрешер, дедуп выхода). Застрявшее уходит в DLQ. Отладка — сквозная: traceparent через весь путь, трейсы в Jaeger (наблюдаемость).

Наращивание по мере боли

Слои добавляются, когда боль реальна (концепт, раздел 10), кроме невозвратного фундамента (брокер с персистентностью, именование, correlation ID, контракт в git). Каталог — когда участников несколько; версионирование — на первом ломающем; трейсинг/DLQ — когда отладка становится болью. Так система не переусложняется вперёд нужды.