---
title: Архитектура
description: C4-обзор Enterium — модульный монолит с CI-принудительными bounded contexts, React web UI, неизменяемыми нативными пайплайнами и вендорнутыми сервисами публикации.
locale: ru
version: 0.2.0
canonical: https://enterium-developer-docs.pages.dev/developers/ru/platform/architecture/
---

> Documentation index: https://enterium-developer-docs.pages.dev/developers/llms.txt — every page of this portal has a Markdown twin at <route>.md; the frozen API contract is at https://enterium-developer-docs.pages.dev/developers/api/openapi.json.

Enterium — **модульный монолит**: один FastAPI-бэкенд, разрезанный на
bounded contexts с принудительными границами в CI, плюс React web UI,
набор неизменяемых нативных пайплайнов, исполняемых in-process, и два
небольших вендорнутых сервиса для публикации и медиа.

## Системный контекст

```mermaid
flowchart TB
    Owner[Owner / team] --> WEB[Web UI]
    Agent[LLM agents] -- MCP + REST --> API
    WEB --> API[Platform API]
    API --> DB[(PostgreSQL<br/>+ pgvector)]
    API --> REDIS[(Redis)]
    API --> EXT[External providers<br/>LLM · search · images · storage]
    API -- render + deploy --> HUB[Publishing hub]
    HUB --> SITES[Fleet of static sites]
    SITES --> GSC[Search consoles]
    GSC -- daily metrics --> API
```

Всё состояние принадлежит API. Внешние провайдеры доступны только через
контекст `integrations` (единый аудируемый канал); публикуемые сайты —
статические, их собирает и деплоит publishing hub; поисковые метрики
возвращаются по расписанию.

## Контейнеры

```mermaid
flowchart TB
    WEBUI["webui<br/>React 19 + TypeScript, Vite"] --> CONTRACTS["contracts<br/>shared OpenAPI + JSON schemas"]
    CONTRACTS --> Backend
    subgraph Backend["platform — FastAPI, Python 3.12"]
      direction TB
      CTX["Bounded contexts<br/>integrations · runs · research · retrieval · quality · publish<br/>analytics · competitors · cockpit · ops · tickets · tasks · chatlog · specs"]
      NATIVE["Native pipelines<br/>immutable versioned dirs: collect · generate · localize"]
      FLAT["Shared layers<br/>auth · lifecycle engine · ORM · API routers"]
    end
    Backend --> SVC1["wp_deployer<br/>publishing hub service"]
    Backend --> SVC2["imagegen<br/>media generation service"]
```

## Правила, которые держат монолит монолитом, а не клубком

1. **Bounded context — вертикальный срез** одной бизнес-способности: его
   логика, схемы, роутер и тесты живут в одной папке
   (`platform/app/contexts/<name>/`).
2. **Контексты общаются только через контракты.** Контекст вызывает другой
   только через его публичный `contracts.py` (типизированный интерфейс)
   или событие — никогда импортом внутренностей, никогда запросом к чужим
   таблицам. Это принуждает import-linter в CI: нарушение — красный билд,
   а не замечание в ревью.
3. **Одна таблица — один контекст-владелец.** Никаких cross-context JOIN.
4. **Новая способность = новый модуль, не новый сервис.** Отдельный сервис —
   исключение, требующее нескольких жёстких триггеров (другой рантайм,
   изоляция секретов, независимое масштабирование). Два вендорнутых
   сервиса существуют ровно потому, что эти триггеры выполнили.
5. **Shared kernel остаётся тонким** — конфигурация, база, примитивы
   аутентификации, логирование, событийная обвязка. Без бизнес-логики.

Полная карта контекстов с зонами ответственности — на странице
[Bounded contexts](../contexts/).

## Модель исполнения

**Постоянно работающего оркестратора-демона нет.** Прод живёт на ночном
таймере, который прогоняет цикл Collect → Generate → publish по флоту
контрактов; между прогонами платформа простаивает по дизайну (статус
`orchestrator.present: false` — норма, см.
[Как интерпретировать статус](../../mcp/interpreting-status/)). Каждое
исполнение записывается как `pipeline_run` с построчными `run_step` —
история запрашивается, а не выводится.

Сами пайплайны — **нативные и in-process**: каждая версия — неизменяемая
директория Python-шагов, резолвящаяся через каталог пайплайнов. Отказы
громкие: ошибка шага роняет прогон и фиксируется в поверхности ошибок;
тихих пошаговых ретраев нет (транзиентные ошибки провайдера ретраит
только LLM-адаптер внутри себя).
