Архитектура
Enterium — модульный монолит: один FastAPI-бэкенд, разрезанный на bounded contexts с принудительными границами в CI, плюс React web UI, набор неизменяемых нативных пайплайнов, исполняемых in-process, и два небольших вендорнутых сервиса для публикации и медиа.
Системный контекст
Заголовок раздела «Системный контекст»Всё состояние принадлежит API. Внешние провайдеры доступны только через
контекст integrations (единый аудируемый канал); публикуемые сайты —
статические, их собирает и деплоит publishing hub; поисковые метрики
возвращаются по расписанию.
Контейнеры
Заголовок раздела «Контейнеры»Правила, которые держат монолит монолитом, а не клубком
Заголовок раздела «Правила, которые держат монолит монолитом, а не клубком»- Bounded context — вертикальный срез одной бизнес-способности: его
логика, схемы, роутер и тесты живут в одной папке
(
platform/app/contexts/<name>/). - Контексты общаются только через контракты. Контекст вызывает другой
только через его публичный
contracts.py(типизированный интерфейс) или событие — никогда импортом внутренностей, никогда запросом к чужим таблицам. Это принуждает import-linter в CI: нарушение — красный билд, а не замечание в ревью. - Одна таблица — один контекст-владелец. Никаких cross-context JOIN.
- Новая способность = новый модуль, не новый сервис. Отдельный сервис — исключение, требующее нескольких жёстких триггеров (другой рантайм, изоляция секретов, независимое масштабирование). Два вендорнутых сервиса существуют ровно потому, что эти триггеры выполнили.
- Shared kernel остаётся тонким — конфигурация, база, примитивы аутентификации, логирование, событийная обвязка. Без бизнес-логики.
Полная карта контекстов с зонами ответственности — на странице Bounded contexts.
Модель исполнения
Заголовок раздела «Модель исполнения»Постоянно работающего оркестратора-демона нет. Прод живёт на ночном
таймере, который прогоняет цикл Collect → Generate → publish по флоту
контрактов; между прогонами платформа простаивает по дизайну (статус
orchestrator.present: false — норма, см.
Как интерпретировать статус). Каждое
исполнение записывается как pipeline_run с построчными run_step —
история запрашивается, а не выводится.
Сами пайплайны — нативные и in-process: каждая версия — неизменяемая директория Python-шагов, резолвящаяся через каталог пайплайнов. Отказы громкие: ошибка шага роняет прогон и фиксируется в поверхности ошибок; тихих пошаговых ретраев нет (транзиентные ошибки провайдера ретраит только LLM-адаптер внутри себя).