ADR-0001 — C4 model rendered as hand-authored D2¶
- Status: Accepted
- Date: 2026-04-28
Context¶
Architecture diagrams in Markdown drift quickly. We want a stable visual language for the platform's structure (systems, containers, components, relationships) that survives team turnover and renders cleanly in MkDocs Material.
Three options were considered:
- Hand-authored Mermaid in Markdown. Easy to write, zero tooling, but expressive ceiling is low — large container diagrams become unreadable, and styling is mostly out of our hands.
- Structurizr DSL → static export. Single model, automated rendering. Tried it; the default PlantUML output looked dated, and the DSL → D2 converter we prototyped was a maintenance burden of its own — every new view shape needed converter work before it could be drawn.
- Hand-authored D2 per diagram. Each C4 view is its own
.d2file, sharing styles via a single_styles.d2partial. No model layer, no converter; the diagrams are the source of truth.
Decision¶
Adopt the C4 model as a documentation framing (Context / Container / Component levels), with each diagram hand-authored as D2 under d2/*.d2 and rendered to SVG by tools/render-diagrams.sh (D2 in Docker, ELK layout, theme 200).
A shared d2/_styles.d2 partial holds the visual vocabulary — palette, classes, edge categories — so every diagram looks like part of the same set without a model layer above. Authoring rules live in d2/STYLE.md.
Sequence diagrams and ER diagrams stay as inline Mermaid in Markdown — D2 is wrong for those shapes.
Consequences¶
- Positive: One file per diagram, readable in isolation. No build step beyond
d2itself. - Positive: D2 SVGs render cleanly in MkDocs Material; the visual style is refreshable by editing
_styles.d2.svg-pan-zoommakes density acceptable in the browser. - Positive: No converter to maintain. Every D2 feature is available immediately; layout tuning is local to the diagram.
- Negative: Diagrams can drift relative to each other if a relationship changes — e.g. a new edge added in
containers.d2must be propagated by hand into the relevant L3 component diagram. Mitigated by review checklist andSTYLE.md. - Negative: No machine-readable model; we can't query "every place X talks to Y" the way a Structurizr workspace would let us.
- Negative: D2's auto-layout sometimes needs manual
direction:/ grid hints; this is expected and called out inSTYLE.md.
Implementation¶
- D2 sources:
d2/*.d2(one file per C4 view;d2/_styles.d2is the shared partial). - Render script:
tools/render-diagrams.sh(and.ps1for Windows). Runsterrastruct/d2in Docker, ELK layout, theme 200, then injects explicitwidth/heightso the browser renders at native size for pan/zoom. - Output:
docs/assets/diagrams/*.svg(gitignored — built locally and in CI). - Style and authoring rules:
d2/STYLE.md.