{
// Bausteinsicht architecture model
"specification": {
"elements": {
"actor": { "notation": "Actor", "description": "A person or external system" },
"system": { "notation": "Software System", "description": "A top-level system", "container": true },
"container": { "notation": "Container", "description": "A deployable unit", "container": true },
"component": { "notation": "Component", "description": "A logical grouping", "container": true }
}
},
"model": {
"customer": {
"kind": "actor",
"title": "Customer",
"description": "End user of the webshop"
},
"webshop": {
"kind": "system",
"title": "Webshop",
"children": {
"api": {
"kind": "container",
"title": "REST API",
"technology": "Spring Boot"
},
"db": {
"kind": "container",
"title": "Database",
"technology": "PostgreSQL"
}
}
}
},
"relationships": [
{ "from": "customer", "to": "webshop.api", "label": "uses" },
{ "from": "webshop.api", "to": "webshop.db", "label": "reads/writes" }
]
}
ADR-001: Choice of DSL Format for Bausteinsicht
Status
Accepted
Context
Bausteinsicht is an architecture-as-code tool that uses draw.io as its visual frontend. A central element is the DSL (Domain-Specific Language) in which the architecture model is defined. The DSL must support bidirectional synchronization with draw.io.
The choice of DSL format significantly influences:
-
How quickly new users become productive
-
What IDE support we get for free vs. need to build ourselves
-
How well LLMs (AI agents) can read and write the DSL
-
How much development effort goes into parser and tooling
Constraints
-
Target audience: developers and software architects
-
Bidirectional sync with draw.io requires stable IDs and machine-readable structure
-
LLM-assisted usage is a key goal (CLI commands for AI agents)
Evaluated Options
Option A: JSON with JSON Schema (JSONC)
The architecture is described in JSON files with comments (JSONC). A JSON Schema defines the structure and enables automatic validation and IDE support.
-
No custom parser needed — every language has JSON support
-
IDE support via JSON Schema for free in all editors (VS Code, IntelliJ, Neovim, …)
-
Publication on SchemaStore.org enables automatic schema recognition
-
LLMs generate JSON natively and reliably
-
JSONC allows comments for better readability
-
Downside: more verbose than a custom DSL
Option B: TypeScript DSL (npm Package)
The architecture is defined as TypeScript code that imports a Bausteinsicht library.
import { model, actor, system, container, rel } from "bausteinsicht";
const customer = actor("customer", {
title: "Customer",
description: "End user of the webshop"
});
const webshop = system("webshop", {
title: "Webshop",
children: {
api: container("api", {
title: "REST API",
technology: "Spring Boot"
}),
db: container("db", {
title: "Database",
technology: "PostgreSQL"
})
}
});
rel(customer, webshop.api, "uses");
rel(webshop.api, webshop.db, "reads/writes");
-
No custom parser needed — publish an npm package
-
Excellent IDE support everywhere TypeScript is supported
-
LLMs generate TypeScript very reliably (large training corpus)
-
Type safety provides autocompletion and error checking
-
Downside: looks like code, not configuration
-
Downside: bidirectional sync harder (code generation on back-sync from draw.io)
-
Downside: Node.js runtime required
Option C: Custom DSL (via Langium) — Reference
A purpose-built language optimized for architecture modeling. Parser and LSP server are generated using Langium.
specification {
element actor
element system
element container
element component
}
model {
customer = actor "Customer" {
description "End user of the webshop"
}
webshop = system "Webshop" {
api = container "REST API" {
technology "Spring Boot"
}
db = container "Database" {
technology "PostgreSQL"
}
api -> db "reads/writes"
}
customer -> webshop.api "uses"
}
-
Maximum control over syntax and readability
-
Potentially the most compact and elegant representation
-
Langium generates LSP server and VS Code extension
-
Downside: weeks of development effort for parser, LSP, VS Code extension
-
Downside: LLMs do not know the syntax (no training corpus)
-
Downside: IDE support only in editors with LSP support
-
Downside: Langium ties to TypeScript/Node.js
Option D: Structurizr DSL
The established DSL from the Structurizr ecosystem (Simon Brown / C4 model). Used by Structurizr, Structurizr Lite, and various community tools.
workspace {
model {
customer = person "Customer" "End user of the webshop"
webshop = softwareSystem "Webshop" {
api = container "REST API" "Handles HTTP requests" "Spring Boot"
db = container "Database" "Stores data" "PostgreSQL"
}
customer -> webshop.api "uses"
webshop.api -> webshop.db "reads/writes"
}
views {
systemContext webshop "Context" {
include *
autoLayout
}
}
}
-
Well-established in the C4 community with large user base
-
Rich ecosystem: Structurizr, Structurizr Lite, CLI, various exporters
-
Views, deployment, and styling built into the DSL
-
Downside: custom parser required (no standard grammar format)
-
Downside: tightly coupled to the C4 model’s fixed 4 levels (person, softwareSystem, container, component)
-
Downside: bidirectional sync not designed for — DSL is source-of-truth only
-
Downside: LLMs know the syntax but make frequent mistakes with its specific keywords
Option E: LikeC4 DSL
A newer C4-inspired DSL with a modern developer experience. Uses Langium for parsing, ships with a VS Code extension and a web-based viewer.
specification {
element actor
element system
element container
element component
}
model {
customer = actor 'Customer' {
description 'End user of the webshop'
}
webshop = system 'Webshop' {
api = container 'REST API' {
technology 'Spring Boot'
}
db = container 'Database' {
technology 'PostgreSQL'
}
api -> db 'reads/writes'
}
customer -> webshop.api 'uses'
}
views {
view context of webshop {
include *
}
}
-
Flexible element hierarchy (not limited to C4’s 4 levels) — similar to Bausteinsicht
-
Modern tooling: live preview, VS Code extension, web viewer
-
Specification-based element kinds (user-defined, not hardcoded)
-
Downside: custom parser required (Langium-based, TypeScript/Node.js)
-
Downside: relatively new project, smaller community than Structurizr
-
Downside: LLMs have very limited training data for this syntax
-
Downside: bidirectional sync not a design goal — DSL is source-of-truth only
Option F: PlantUML C4
Architecture modeling using PlantUML with the C4-PlantUML extension library. Leverages the widely-used PlantUML rendering engine.
@startuml
!include <C4/C4_Context>
Person(customer, "Customer", "End user of the webshop")
System_Boundary(webshop, "Webshop") {
Container(api, "REST API", "Spring Boot", "Handles HTTP requests")
Container(db, "Database", "PostgreSQL", "Stores data")
}
Rel(customer, api, "uses")
Rel(api, db, "reads/writes")
@enduml
-
Massive ecosystem: PlantUML is one of the most widely used diagramming tools
-
LLMs generate PlantUML very reliably (huge training corpus)
-
Renders directly to PNG/SVG without external tools
-
Supported by many documentation frameworks (AsciiDoc, Markdown, Confluence)
-
Downside: macro-based syntax is not a true data model — difficult to parse reliably
-
Downside: bidirectional sync essentially impossible (rendering language, not a data model)
-
Downside: no JSON Schema or structured validation possible
-
Downside: element IDs are positional macro arguments, not stable keys
-
Downside: fixed to C4 macros (Person, Container, Component) — no custom element kinds
Weighted Pugh Matrix
Rating scale: -1 = worse than reference, 0 = same as reference, +1 = better than reference
Reference option: C (Custom DSL)
| Criterion | Weight | A: JSON+Schema | B: TypeScript | C: Custom DSL (Ref) | D: Structurizr | E: LikeC4 | F: PlantUML C4 |
|---|---|---|---|---|---|---|---|
Learnability |
5 |
+1 |
-1 |
0 |
0 |
0 |
+1 |
IDE support (breadth & effort) |
5 |
+1 |
+1 |
0 |
-1 |
0 |
+1 |
LLM friendliness |
4 |
+1 |
+1 |
0 |
0 |
-1 |
+1 |
Parser / tooling effort |
4 |
+1 |
+1 |
0 |
0 |
0 |
-1 |
Bidirectional sync suitability |
5 |
+1 |
-1 |
0 |
-1 |
-1 |
-1 |
Syntax compactness |
3 |
-1 |
-1 |
0 |
0 |
0 |
-1 |
Portability (no runtime) |
2 |
+1 |
-1 |
0 |
+1 |
-1 |
+1 |
Expressiveness |
2 |
-1 |
+1 |
0 |
-1 |
+1 |
-1 |
Weighted Results
| Criterion | A: JSON+Schema | B: TypeScript | C: Custom DSL (Ref) | D: Structurizr | E: LikeC4 | F: PlantUML C4 |
|---|---|---|---|---|---|---|
Learnability (×5) |
+5 |
-5 |
0 |
0 |
0 |
+5 |
IDE support (×5) |
+5 |
+5 |
0 |
-5 |
0 |
+5 |
LLM friendliness (×4) |
+4 |
+4 |
0 |
0 |
-4 |
+4 |
Parser / tooling effort (×4) |
+4 |
+4 |
0 |
0 |
0 |
-4 |
Bidirectional sync (×5) |
+5 |
-5 |
0 |
-5 |
-5 |
-5 |
Syntax compactness (×3) |
-3 |
-3 |
0 |
0 |
0 |
-3 |
Portability (×2) |
+2 |
-2 |
0 |
+2 |
-2 |
+2 |
Expressiveness (×2) |
-2 |
+2 |
0 |
-2 |
+2 |
-2 |
Total |
+20 |
0 |
0 |
-10 |
-9 |
+2 |
Decision
Option A: JSON with JSON Schema (JSONC)
JSON with JSON Schema offers the best overall package for Bausteinsicht:
-
Immediate start: No parser development effort — we can focus on the core feature (draw.io synchronization).
-
Bidirectional sync: JSON is trivial to read, modify, and write back. With TypeScript code, back-generation from draw.io is significantly more complex.
-
Universal IDE support: JSON Schema provides autocompletion, validation, and hover documentation in all major editors with zero extra effort.
-
LLM-native: LLMs generate JSON more reliably than any other format. This enables AI-assisted architecture maintenance via CLI commands.
-
Comments via JSONC: The main argument against JSON (no comments) is mitigated by JSONC.
The lower compactness compared to a custom DSL is an acceptable trade-off given the massive advantages in tooling, sync, and LLM integration.
Consequences
Positive
-
Fast project start without parser development
-
IDE support in all editors from day 1
-
LLMs can reliably read and write architecture files
-
JSON Schema serves as living documentation of the model format
-
Easy integration into CI/CD pipelines (JSON validation)
Negative
-
Architecture files are more verbose than a custom DSL
-
JSON syntax requires braces and quotes (more typing)
-
No native variables or references within the DSL
-
Relationships must be expressed via string IDs (
"from": "webshop.api") rather than direct references
Risks
-
If verbosity becomes too disruptive in practice, a converter from a more compact syntax to JSON can be built later (the JSON foundation remains)
-
JSONC is not supported by all JSON parsers — we need to choose a JSONC-capable parser
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.