К содержимому
МАТЕРИАЛЫ КУРСА
НАВИГАЦИЯ

C4: четыре уровня архитектуры

Как описывать архитектуру AI-продукта: от контекста к контейнерам и компонентам, только PlantUML, только as-code

Право «нормально вертеть проект» зарабатывается пониманием архитектуры: если ты свободно переходишь между уровнями C4 — от «кто нами пользуется» до «какой модуль за что отвечает», — любое изменение проекта перестаёт быть страшным.

В 2026 у архитектуры as-code есть второй заказчик: агент. Диаграмма в PlantUML, лежащая в репозитории, — это карта проекта, которую читает и человек, и Claude Code, и Vibe. Агент с актуальной container-диаграммой делает на порядок меньше архитектурного бреда.

Правила игры

  1. Только as-code. Исходник .puml лежит в docs/diagrams/, сгенерированный .svg — рядом, оба закоммичены. Figma и Miro не используются.
  2. Диаграмма отражает то, что есть, а не то, что мечталось. Разъехались — чинишь диаграмму в том же PR, что и код.
  3. Один уровень — одна диаграмма. Не смешивай контейнеры с компонентами на одной картинке: это главный источник каши.

Генерация SVG:

plantuml -tsvg docs/diagrams/*.puml

Три уровня — от мира к модулям

Пролистай: диаграмма закрепляется, а по мере чтения детализируется — от системы среди внешнего мира до модулей внутри одного контейнера.

C4 Context

01Context — система и мир

Показываем: систему одним прямоугольником, живых людей, внешние системы, направления связей.

Не показываем: технологии, базы данных, внутренности.

Типичные ошибки: контейнеры на контекстной диаграмме («а вот тут у нас PostgreSQL»); забытые внешние системы (LLM-провайдер и Langfuse — тоже внешние); связи без подписей.

C4 Container

02Container — из чего состоит система

Показываем: самостоятельно запускаемые единицы (frontend, API, БД, AI-модуль), технологию каждого, протоколы связи.

Тест на контейнер: запускается отдельно ИЛИ хранит данные — контейнер; иначе — компонент. Контейнер ≠ Docker-контейнер: слои controller/service/repository живут в одном процессе — это компоненты.

Это главная диаграмма проекта — именно её проверяет aact checkи именно она нужна агенту в контексте.

C4 Component

03Component — внутри одного контейнера

Показываем: крупные модули одного контейнера и их обязанности: роутеры, сервисы, доступ к данным, шлюз к модели.

Правило одного предложения: если обязанность компонента не описывается одним предложением без «и» — это два компонента. AI Gateway — единая точка вызова модели с ретраями, таймаутами и лимитами токенов.

Уровень 4 — Code: не рисуем

Диаграммы классов устаревают быстрее, чем их успевают дорисовать. Уровень кода — это сам код: структура папок, типы и сигнатуры. Если очень нужно показать связи — генерируй из кода, не рисуй руками.

Процедура: от описания к модели

Порядок из скилла aact-architect — строго по шагам, не забегая вперёд:

  1. Назови систему — одна система, одна System_Boundary.
  2. Люди и внешние системы — кто пользуется (Person), от чего зависим (System_Ext: LLM-провайдер, Langfuse — тоже сюда).
  3. Запускаемые единицы и хранилища — каждая проходит тест «запускается отдельно или хранит данные».
  4. Связи — каждая с подписью что течёт и как: Rel(web, api, "fetches orders", "JSON/HTTPS"), а не Rel(web, api).
  5. Границы — только если контекстов реально больше одного.
  6. Стоп. На уровень компонентов — только когда понадобилось разобрать внутренности одного контейнера.

Связка с ADR: решение, диаграмма и цена

Каждое значимое изменение диаграммы — следствие решения, а у решения есть цена. Берём шаблон ADR из aact и добавляем одно обязательное поле:

docs/adr/ADR-002-model-choice.md
# Выбор модели для генерации вопросов

## Контекст
<в каких условиях принимается решение: объём, бюджет, требования к качеству>

## Краткое описание решения и его обоснование

### Схема
<фрагмент C4-диаграммы, на который влияет решение>

### Решение
Используем <модель> через <провайдера>, деградация на <дешёвую модель>.

### Обоснование
<почему так, что рассматривали ещё и почему отказались>

## Цена решения
<какой долг берём: стоимость запроса, привязка к провайдеру, лимиты>

## Как покрыть тестами
<какая проверка поймает, что решение перестало соблюдаться>

### Примеры тестов
<ссылки на тесты или evals>

Два раздела здесь делают всю работу. «Как покрыть тестами» — из шаблона aact, и это его лучшая идея: решение, которое нечем проверить, через два спринта существует только на бумаге. «Цена решения» — наше добавление и условие DoD: ADR с пустым полем цены не принимается. Так технический долг остаётся видимым, а не растворяется в «ну мы же обсуждали».

Диаграмма показывает «как», ADR отвечает «почему именно так и что нам это стоит». Живые примеры — в ADRs репозитория. Файл называй по решению, а не по технологии: «Anti-corruption Layer.md», а не «Добавили Kafka.md» — через полгода искать будут первое.