C4: четыре уровня архитектуры
Как описывать архитектуру AI-продукта: context → container → component → code, только PlantUML, только as-code
C4: четыре уровня архитектуры
Право «нормально вертеть проект» зарабатывается пониманием архитектуры: если ты свободно переходишь между уровнями C4 — от «кто нами пользуется» до «какой модуль за что отвечает», — любое изменение проекта перестаёт быть страшным.
В 2026 у архитектуры as-code есть второй заказчик: агент. Диаграмма в PlantUML, лежащая в репозитории, — это карта проекта, которую читает и человек, и Vibe/Claude Code. Агент с актуальной container-диаграммой делает на порядок меньше архитектурного бреда.
Правила игры
- Только as-code. Исходник
.pumlлежит вdocs/diagrams/, сгенерированный.svg— рядом, оба закоммичены. Figma и Miro не используются. - Диаграмма отражает то, что есть, а не то, что мечталось. Разъехались — чинишь диаграмму в том же PR, что и код.
- Один уровень — одна диаграмма. Не смешивай контейнеры с компонентами на одной картинке: это главный источник каши.
Генерация SVG:
plantuml -tsvg docs/diagrams/*.pumlУровень 1 — Context: система и мир
Показываем: систему одним прямоугольником, живых людей, внешние системы, направления связей.
Не показываем: технологии, базы данных, внутренности.
Типичные ошибки: контейнеры на контекстной диаграмме («а вот тут у нас PostgreSQL»); забытые внешние системы (LLM-провайдер и Langfuse — тоже внешние системы); связи без подписей.
Уровень 2 — Container: из чего состоит система
Показываем: самостоятельно запускаемые единицы (frontend, API, БД, AI-модуль), технологию каждого, протоколы связи.
Не показываем: классы, модули, файлы — это уровень ниже.
Тест на контейнер: запускается отдельно ИЛИ хранит данные — контейнер; иначе — компонент. Контейнер ≠ Docker-контейнер (c4model.com прямо пишет «Not Docker!»): слои controller/service/repository живут в одном процессе — это компоненты; очередь и топик — контейнеры, а вот брокер целиком — нет.
Типичные ошибки: «AI» как магическое облако без границ (модель — это внешний провайдер, а AI-модуль — ваш контейнер); стрелки «все со всеми» без протоколов; больше ~20 коробок — значит, ты уже рисуешь компоненты.
Это главная диаграмма проекта — именно её проверяет aact check,
и именно она нужна агенту в контексте.
Уровень 3 — Component: внутри одного контейнера
Показываем: крупные модули одного контейнера и их обязанности: роутеры, сервисы, доступ к данным, шлюз к модели.
Не показываем: отдельные функции и классы.
Правило одного предложения: если обязанность компонента не описывается одним предложением без «и» — это два компонента. AI Gateway на диаграмме — осознанный паттерн: единая точка вызова модели, где живут ретраи, таймауты и лимиты токенов, а не размазанные по коду вызовы API.
Уровень 4 — Code: не рисуем
Диаграммы классов устаревают к обеду. Уровень кода — это сам код: структура папок, типы и сигнатуры. Если очень нужно показать связи — генерируй из кода, не рисуй руками.
Процедура: от описания к модели
Порядок из скилла aact-architect — строго по шагам, не забегая вперёд:
- Назови систему — одна система, одна
System_Boundary. - Люди и внешние системы — кто пользуется (
Person), от чего зависим (System_Ext: LLM-провайдер, Langfuse — тоже сюда). - Запускаемые единицы и хранилища — каждая проходит тест «запускается отдельно или хранит данные».
- Связи — каждая с подписью что течёт и как:
Rel(web, api, "fetches orders", "JSON/HTTPS"), а неRel(web, api). - Границы — только если контекстов реально больше одного.
- Стоп. На уровень компонентов — только когда понадобилось разобрать внутренности одного контейнера.
Связка с ADR: решение → диаграмма → цена
Каждое значимое изменение диаграммы — следствие решения, а у решения есть цена. Минимальный ADR:
# ADR-002: Выбор модели для генерации
## Решение
Используем <модель> через <провайдера>, деградация на <дешёвую модель>.
## Альтернативы
<что рассматривали и почему отказались>
## Цена решения
<какой долг берём: стоимость, привязка к провайдеру, лимиты>ADR без заполненной «Цены решения» не проходит DoD. Диаграмма показывает «как», ADR отвечает «почему именно так и что нам это стоит».
Канонический шаблон (Status / Context / Decision / Consequences) и живые примеры решений — в ADRs репозитория aact; наша «Цена решения» — это Consequences, вынесенные в обязательное поле. Файл ADR называй по решению, а не по технологии: «Anti-corruption Layer.md», а не «Добавили Kafka.md».