This post continues from the second part.
That introduced bausteinsicht init, sync, and watch.
Here the focus is on the heart of it: the JSONC data model with all fields and extension points.
The Basic Structure
An architecture.jsonc file has four required fields and several optional sections:
{
"$schema": "./node_modules/.bausteinsicht/bausteinsicht.schema.json",
"config": { ... },
"specification": { ... },
"model": { ... },
"relationships": [ ... ],
"views": { ... },
"asIs": { ... },
"toBe": { ... },
"dynamicViews": [ ... ],
"constraints": [ ... ]
}config
Global settings for diagram generation:
"config": {
"author": "Paul Fleischmann",
"repo": "https://github.com/my-org/my-repo",
"metadata": true,
"legend": true
}specification
The specification defines the language of the model — which element types and relationship kinds exist.
It is the only section that needs to be set up once and then rarely changed.
specification.elements
"specification": {
"elements": {
"actor": {
"notation": "Actor",
"description": "A user or external system",
"container": false
},
"system": {
"notation": "Software System",
"container": true
},
"container": {
"notation": "Container",
"container": true
},
"component": {
"notation": "Component",
"container": true
}
}
}The container flag controls whether an element may have children in the hierarchy.
If the flag is missing (or is false), bausteinsicht validate rejects any model element of that type that has children.
specification.relationships
"specification": {
"relationships": {
"uses": {
"notation": "uses",
"dashed": false
},
"reads": {
"notation": "reads",
"dashed": true
}
}
}specification.tags
Tags are defined under specification.tags and can optionally carry draw.io styling:
"specification": {
"tags": [
{
"id": "external",
"description": "External system outside our control",
"style": {
"fillColor": "#f5f5f5",
"fontColor": "#666666"
}
},
{
"id": "legacy",
"description": "Legacy system — no active further development"
}
]
}specification.patterns
Patterns are reusable topology templates — for example, a standard microservice pattern with frontend, API, and database:
"specification": {
"patterns": {
"microservice": {
"description": "Standard pattern: API + Database",
"elements": [
{ "id": "api", "kind": "container", "title": "{{name}} API", "technology": "Go" },
{ "id": "db", "kind": "container", "title": "{{name}} Database", "technology": "PostgreSQL" }
],
"relationships": [
{ "id": "r1", "from": "api", "to": "db", "label": "reads/writes", "kind": "uses" }
]
}
}
}specification.decisions
ADR references (Architecture Decision Records) can be managed directly in the specification:
"specification": {
"decisions": [
{
"id": "ADR-001",
"title": "PostgreSQL as the primary database",
"status": "active",
"date": "2025-01-15",
"file": "docs/adr/001-postgresql.md"
}
]
}model
The model describes the concrete elements of the system.
Each key is the element ID — it must be unique within its hierarchy level.
Element Fields
"model": {
"onlineshop": {
"kind": "system",
"title": "Online Shop",
"description": "The core of the system",
"technology": "Go, PostgreSQL",
"tags": ["external", "legacy"],
"status": "deployed",
"decisions": ["ADR-001"],
"metadata": {
"owner": "Team Backend",
"cost": "€500/month"
},
"children": {
"frontend": { ... },
"api": { ... }
}
}
}Nested Hierarchy
Elements can be nested to any depth, as long as the respective kind has container: true:
"model": {
"shop": {
"kind": "system", "title": "Online Shop",
"children": {
"backend": {
"kind": "container", "title": "Backend",
"children": {
"orderservice": { "kind": "component", "title": "Order Service" },
"paymentservice": { "kind": "component", "title": "Payment Service" }
}
}
}
}
}The element ID in relationships and views uses dot notation for the path: shop.backend.orderservice.
status Values
| Status | Meaning | draw.io Color |
|---|---|---|
| Planned, not yet started | Yellow |
| In design | Blue |
| Under development | Orange |
| In production | Green |
| Outdated, being replaced | Red |
| Decommissioned | Gray |
relationships
Relationships are defined as a flat array at the top level, not inside elements. This allows relationships between elements at different hierarchy levels:
"relationships": [
{
"from": "shop.backend.orderservice",
"to": "shop.backend.paymentservice",
"label": "confirms payment",
"kind": "uses",
"description": "Synchronous REST call",
"cardinality": "1:N",
"dataFlow": "sync",
"decisions": ["ADR-002"]
}
]views
Views define which elements appear in which diagram tab.
Each key becomes a tab in architecture.drawio.
View Fields
"views": {
"context": {
"title": "System Context",
"description": "Overview of all systems",
"scope": "shop",
"include": ["customer", "shop"],
"exclude": ["shop.backend.legacy"],
"filter-tags": ["external"],
"exclude-tags": ["archived"],
"layout": "auto"
}
}include with wildcards:
"include": [
"customer",
"shop.*",
"shop.**"
]Optional Extensions
asIs / toBe
For as-is/to-be comparisons, the current and target architecture can be stored directly in the model:
"asIs": {
"elements": {
"legacy-monolith": { "kind": "system", "title": "Legacy Monolith" }
},
"relationships": [
{ "from": "customer", "to": "legacy-monolith", "label": "uses" }
]
},
"toBe": {
"elements": {
"shop": { "kind": "system", "title": "Online Shop (Microservices)" }
},
"relationships": [
{ "from": "customer", "to": "shop", "label": "uses" }
]
}bausteinsicht diff visualizes the difference between asIs and toBe as an annotated diagram.
dynamicViews
Sequence diagrams describe the temporal flow of interactions:
"dynamicViews": [
{
"key": "checkout",
"title": "Checkout Flow",
"steps": [
{ "index": 1, "from": "customer", "to": "frontend", "label": "clicks Buy", "type": "sync" },
{ "index": 2, "from": "frontend", "to": "orderservice", "label": "POST /orders", "type": "sync" },
{ "index": 3, "from": "orderservice", "to": "paymentservice", "label": "charge()", "type": "sync" },
{ "index": 4, "from": "paymentservice", "to": "orderservice", "label": "OK", "type": "return" }
]
}
]type values: sync (solid arrow) | async (open arrow) | return (dashed back).
bausteinsicht export sequence generates PlantUML or Mermaid from this.
constraints
Architecture rules are defined as machine-checkable constraints and evaluated with bausteinsicht lint:
"constraints": [
{
"id": "NO-DIRECT-DB-ACCESS",
"description": "Only services may access the database directly",
"rule": "no-relationship",
"from-kind": "component",
"to-kind": "database"
},
{
"id": "REQUIRE-DESCRIPTION",
"description": "All systems must have a description",
"rule": "required-field",
"element-kind": "system",
"field": "description"
}
]Available rule values: no-relationship, allowed-relationship, required-field, max-depth, technology-allowed.
JSON Schema for IDE Auto-Completion
Without a plugin, auto-completion works through the bundled JSON schema.
Set the $schema entry in architecture.jsonc:
{
"$schema": "https://raw.githubusercontent.com/docToolchain/Bausteinsicht/main/schemas/bausteinsicht.schema.json"
}In VS Code (and any editor with JSON schema support), auto-completion for all fields appears immediately — without having to install an extension.
Example Model
The example for this part is stored as a runnable JSONC file at teil_3.jsonc and shows specification, model, views, and relationships with all optional fields.
This is what the result looks like in draw.io (bausteinsicht sync):
The draw.io file for this can be found here: teil_3.drawio
Generated PNG files via bausteinsicht export --image-format png:


Generated PlantUML diagrams via bausteinsicht export-diagram:
What Comes Next
Part 4: Bidirectional Synchronization — how the merge algorithm works and what happens when both sides were changed simultaneously
Part 5: Multi-View — multiple diagram perspectives from one model
Official documentation: User Manual · Tutorial on doctoolchain.org