ADR 0068 — Диаграмма C4 Arquiteto
- Статус: принят
- Дата: 2026-08-12
- Контекст: запрос пользователя — Arquiteto получает новый результат, отрисовываемый в Visão Geral проекта
- Расширяет: тот же шаблон артефакта-без-таблицы, что
ADR 0065
(
artifact.project_image)
Контекст
Результаты Arquiteto сегодня — это текст и структурированные данные: module_map
(сетка карточек), ADR (список со ссылкой на PR), insights. Ни один не является изображением
архитектуры. Запрос — диаграмма C4 (модель Саймона Брауна — как минимум
Context и Container), действительно отрисованная на экране, а не ссылка наружу.
Два архитектурных вопроса до написания кода:
Кто пишет уровень Container — модель или репозиторий?
module_map уже существует, проверенный на отсутствие циклов (create_module_map). Позволить
модели ПЕРЕПЕЧАТАТЬ модули и зависимости в tool call диаграммы — риск
тихого расхождения: диаграмма говорила бы одно, действующий module_map
говорил бы другое, и не было бы способа узнать, кто солгал. Альтернатива —
выводить уровень Container из действующего module_map в use case и никогда из того, что
модель пишет заново, — закрывает это расхождение по построению. У уровня Context
такого источника нет: кто внешние акторы (пользователь, git-
provider) — это суждение Arquiteto, без module_map акторов, из которого можно
вывести. Поэтому только Context приходит из tool call.
Чем отрисовывать? В apps/web не было ни одной библиотеки диаграмм
(ноль вхождений "mermaid" в package.json до этого изменения); сайт
документации использует Mermaid, но во время СБОРКИ (Docusaurus). Mermaid — де-факто
стандартный движок для диаграмм-как-текста, включая нативную поддержку
C4Context/C4Container, — мы не пишем движок раскладки. Решение
пользователя, подтверждённое до этого изменения: mermaid входит как новая
RUNTIME-зависимость React-приложения, первая такого рода.
Решение
Диаграмма C4 — версионируемый артефакт в event log, без таблицы, — та же
конструкция, что artifact.project_image (ADR 0065), — с уровнем Container,
ВЫВЕДЕННЫМ из действующего module_map, никогда не перепечатываемым моделью.
artifact.c4_diagram:CreateC4DiagramUseCaseполучает действующийmodule_map(ModuleMapRepository.findCurrent); без него отказывает с 400 — нет уровня Container без модулей. Генерирует оба синтаксиса Mermaid (gerarDiagramaContexto/gerarDiagramaContainer, чистые, вdomain/architecture/c4-diagram.ts) и записывает событие сversion=GetC4DiagramUseCase.execute(projectId).version + 1— как чтение "действующий — с наибольшимversion" вObterContainerDoProjetoUseCase. Повторная выдача — это генерация заново; история не переписывается.- Без regex при генерации синтаксиса: имя модуля/актора приходит от модели (или из
того, что модель уже написала в
create_module_map), иjs/polynomial-redosздесь был бы той же HIGH, которойproject-container.tsуже избегает. Экранирование метки и генерация id Mermaid посимвольные, в том же стиле, чтоreferenciaDeImagemValida. - Инструмент
create_c4_diagramу Arquiteto:system_name(обязательно),system_descriptionиactors(имя +person/external_system+ описание). Тонкий — лишь нормализует и передаёт дальше; валидирует и выводит api, тот же шаблон, чтоcreate_module_map/choose_project_image. mermaidизолирован заapps/web/src/lib/mermaid-render.ts(renderMermaid(id, sintaxe)), с ДИНАМИЧЕСКИМimport()— тот, кто никогда не открывает Visão Geral со сгенерированной диаграммой, не платит за bundle.vite buildподтверждает: eager загружается толькоindex-*.js(entrypoint); чанки Mermaid (mermaid.core,c4Diagram-*и его тяжёлые зависимости —cytoscape.esm435 KB,katex258 KB — используемые только ДРУГИМИ типами диаграмм, которые этот продукт не генерирует) остаются по требованию.C4DiagramView.tsx(apps/web) — три состояния на диаграмму (RN-088):rendering(Skeleton),erro(Alert с сообщением Mermaid + сырой синтаксис, свёрнутый в<details>, НИКОГДА не ломающийся экран) иpronto(SVG черезdangerouslySetInnerHTML— содержимое, которое МЫ генерируем черезmermaid.render, а не переданный HTML третьей стороны, сsecurityLevel: 'strict'в Mermaid). Четвёртое состояние, "диаграммы нет вовсе", принадлежит разделу, вызывающему компонент (ArchitectureSection), как уже существует дляmoduleMap/ADR.- Тема Mermaid читается из токенов дизайн-системы в runtime
(
getComputedStyleпо--surface-*/--text-*/--border*), никогда фиксированным цветом — не потому, что у приложения сегодня есть переключатель темы (его нет: dark основная), а чтобы не хардкодить палитру, которая уже есть вtokens.css.
CSP — подтверждено, без изменений
У nginx образа web закрытый CSP со времён ADR 0058
(script-src 'self', без unsafe-eval/unsafe-inline;
style-src 'self' 'unsafe-inline'). Проверено на продакшен-ассетах
(grep -rl "new Function(\|eval(" dist/assets/*.js): НИ ОДИН чанк
Mermaid не использует eval/new Function. style-src уже имел unsafe-inline
до этого изменения (по другой причине — inline-CSS самого приложения), и именно он
покрывает <style>, который SVG Mermaid может встраивать. Ни одна строка
nginx.conf не изменилась.
Последствия
- Bundle больше, но по требованию.
pnpm buildподтвердил корректное code-splitting: entrypoint (index-*.js) не вырос на вес всего Mermoid — чанки скачивает только тот, кто действительно отрисовывает диаграмму. Реальная стоимость (не измерена в байтах сети, лишь подтверждена как lazy) приходится на первый раз, когда Visão Geral показывает сгенерированную диаграмму. - Только Context и Container. Component и Code (уровни 3 и 4 C4) остаются
вне — у
module_mapнет гранулярности компонентов/кода, а выдумывать эту гранулярность не просили. - Плотная диаграмма не получает особой обработки.
module_mapс десятками модулей даёт большую диаграмму Container; нет виртуализации и сворачивания — тот же срез, что FASE 26 уже сделала для вкладки Code ("a mais custosa do programa"), пока принят. - Расхождение между диаграммой и module_map только временное, а не в данных.
Поскольку Container ВЫВОДИТСЯ в момент генерации, старая диаграмма может
устареть, если
module_mapизменится позже, — но она никогда не будет ЛГАТЬ о том, что существовало в момент генерации, потому что модули описывала не модель.