|
Note
|
This version of the template contains some help and explanations. It is used for familiarization with arc42 and the understanding of the concepts. For documentation of your own system you use better the plain version. |
Template
Unresolved directive in <stdin> - include::chapters/../../../common/styles/arc42-help-style.adoc[]
About arc42
arc42, the template for documentation of software and system architecture.
Template Version {revnumber}. {revremark}, {revdate}
Created, maintained and © by Dr. Peter Hruschka, Dr. Gernot Starke and contributors. See https://arc42.org.
1. Introduction and Goals
Bausteinsicht is an architecture-as-code tool that uses draw.io as its visual frontend. It maintains a structured architecture model in a JSON-based DSL and synchronizes it bidirectionally with draw.io diagrams.
1.1. Requirements Overview
Bausteinsicht addresses the gap between structured architecture models and widely adopted visual diagramming tools.
| Requirement | Description |
|---|---|
Architecture Model |
A JSON-based DSL serves as single source of truth for all architecture elements, relationships, and metadata. |
draw.io Integration |
draw.io serves as the visual frontend. Templates define the styling, fully customizable by the user. |
Bidirectional Sync |
Changes in the model are reflected in draw.io views and vice versa. New elements, updated descriptions, and relationships are synchronized in both directions. |
Flexible Hierarchy |
Element hierarchy is user-defined and not limited to the 4 C4 levels. Components of components are valid. |
CLI & Watch Mode |
A CLI provides commands for sync, validation, and watching. CLI commands also enable LLM-driven architecture maintenance. |
Template System |
Templates are draw.io files containing styled example elements. Users create and customize templates entirely within draw.io. |
Architecture Analysis |
Commands assess the model: constraint checking ( |
Architecture Evolution |
The architecture is versioned and comparable over time: snapshots with semantic diff ( |
Interoperability & Export |
Models import from Structurizr DSL and LikeC4 ( |
IDE Integration |
A Language Server (the separate |
See PRD-001 for the full product requirements.
1.2. Quality Goals
The top three quality goals driving architectural decisions:
| Priority | Quality Goal | Scenario |
|---|---|---|
1 |
Learnability |
A developer unfamiliar with Bausteinsicht creates a working architecture model with draw.io views within 30 minutes using only the documentation and IDE autocompletion. |
2 |
IDE Support |
When editing the JSON model in VS Code, IntelliJ, or Neovim, the editor provides autocompletion, validation, and hover documentation without installing any Bausteinsicht-specific plugin. |
3 |
LLM Friendliness |
An LLM agent (e.g., Claude) can read the architecture model, add a new element with relationships, and trigger synchronization using only CLI commands — without human intervention. |
See Chapter 10 for the full quality requirements and scenarios.
1.3. Stakeholders
| Role/Name | Contact | Expectations |
|---|---|---|
Software Architect |
Primary user |
Maintains architecture models visually in draw.io with a structured model behind the scenes. Expects flexible hierarchy and easy template customization. |
Developer |
Team member |
Consults architecture diagrams in draw.io. Expects diagrams to always reflect the current model state. |
LLM Agent |
AI assistant (e.g., Claude) |
Modifies architecture models via CLI commands. Expects a machine-readable JSON format and predictable CLI behavior. |
1.4. Related Projects
Bausteinsicht exists in the broader landscape of architecture-as-code and C4 modeling tools. The following projects solve similar or overlapping problems:
| Project | Description |
|---|---|
The original C4 model tooling by Simon Brown. Provides a dedicated DSL, web-based renderer, and export to many formats. DSL is source-of-truth (no bidirectional sync with diagrams). |
|
The methodology behind the 4-level architecture abstraction (Context, Container, Component, Code). Bausteinsicht is C4-inspired but supports user-defined, unlimited nesting levels. |
|
A modern C4-inspired tool with a custom DSL, VS Code extension, and live web preview. Supports user-defined element kinds and flexible nesting, similar to Bausteinsicht. |
|
A visual-first architecture diagramming tool with isometric rendering. Focused on visual editing rather than text-based modeling. |
|
Extends the C4 model with interface and flow concepts for documenting system interactions at a more granular level. |
Bausteinsicht’s differentiator is bidirectional synchronization between a text model (JSONC) and draw.io diagrams — edits in either direction are merged. See ADR-001 for a detailed comparison of DSL formats.
2. Architecture Constraints
2.1. Technical Constraints
| Constraint | Description |
|---|---|
Go as implementation language |
The tool is implemented in Go to enable single-binary distribution without runtime dependencies. See ADR-002. |
JSON as model format |
The architecture model uses JSON (JSONC) with JSON Schema for validation. See ADR-001. |
draw.io XML as output format |
The visual representation uses draw.io’s mxGraph XML format. This constrains layout capabilities to what draw.io supports. |
No JavaScript/Node.js in the product |
The CLI and its libraries use no JavaScript/Node.js, avoiding npm supply-chain risk. Exception: the optional VS Code extension ( |
Cross-platform |
The tool must run on Linux, macOS, and Windows. |
Headless draw.io for image export |
|
Git for history-based commands |
|
2.2. Organizational Constraints
| Constraint | Description |
|---|---|
Open Source |
The project is developed as open source software. |
Documentation in English |
All documentation is written in English. |
Documentation in AsciiDoc |
Architecture documentation uses arc42 template in AsciiDoc format. |
ADR format |
Architecture decisions follow the Nygard ADR format with a weighted Pugh matrix for option evaluation. |
2.3. Conventions
| Convention | Description |
|---|---|
Version control |
All files (model JSON, draw.io XML, templates) are text-based and Git-friendly. |
Version numbering |
The tool displays a version number. Semantic versioning is used. |
ADR naming |
ADRs follow the pattern |
3. Context and Scope
3.1. Business Context
Bausteinsicht sits between the architecture model (JSON files) and draw.io diagrams, synchronizing both bidirectionally.
3.1.1. Architecture Maintenance Process
The following activity diagram shows how Bausteinsicht fits into the daily architecture workflow. The architect chooses between editing the text model or the visual diagram — Bausteinsicht synchronizes both directions.
3.1.2. System Context
| Element | Kind | Technology | Description |
|---|---|---|---|
Bausteinsicht |
system |
Architecture-as-code tool with draw.io as visual frontend and bidirectional synchronization |
|
Developer |
actor |
Developer using the Bausteinsicht CLI to manage architecture models |
|
draw.io App |
external_system |
draw.io desktop or web application for visual diagram editing |
|
IDE |
external_system |
IDE with JSON Schema support for JSONC editing with autocompletion |
|
Note
|
The generated diagram above is itself produced from a Bausteinsicht model and shows the principal technical partners (developer, draw.io, IDE). The table below is the authoritative, complete set of communication partners — including the human architect, LLM agents, and git, which act on the system rather than appearing inside its own context view. |
| Communication Partner | Input | Output |
|---|---|---|
Software Architect |
Edits to architecture model (JSONC) or draw.io diagrams |
Synchronized model and diagrams |
Developer |
— |
Up-to-date architecture diagrams in draw.io |
LLM Agent |
CLI commands (add element, sync, validate) |
Updated model files, validation results |
draw.io |
Visual edits to diagram elements and relationships |
Generated/updated diagram XML |
IDE (VS Code, IntelliJ, etc.) |
JSON Schema |
Autocompletion, validation, hover docs for model files |
Git |
Version-controlled model and diagram files |
Diff-friendly text-based file changes |
3.2. Technical Context
| Interface | Technology | Description |
|---|---|---|
Architecture Model |
JSONC files on filesystem |
The model is stored as |
draw.io Diagrams |
mxGraph XML files on filesystem |
Standard |
Templates |
mxGraph XML files on filesystem |
draw.io files containing styled reference elements for each element kind. |
CLI |
Go binary (stdin/stdout) |
Command-line interface for sync, validate, watch, and model manipulation commands. |
File System Watcher |
OS-native (inotify/FSEvents/ReadDirectoryChanges) |
Watches model and diagram files for changes in watch mode. |
JSON Schema |
Published schema file (local or SchemaStore.org) |
Enables IDE support without Bausteinsicht-specific plugins. |
draw.io CLI |
External process (auto-detected on |
|
Git |
External process ( |
|
Import / export formats |
Files on filesystem |
In: Structurizr DSL ( |
Language Server (LSP) |
JSON-RPC over stdio |
The |
Snapshot store |
Files on filesystem ( |
Versioned model captures written and read by the |
4. Solution Strategy
4.1. Technology Decisions
| Decision | Choice | Rationale |
|---|---|---|
Implementation language |
Go (ADR-002) |
Single-binary distribution, LLM-friendly code generation, compile-time type safety |
Architecture model format |
JSONC with JSON Schema (ADR-001) |
Universal IDE support, zero parser effort, LLM-native read/write |
draw.io XML processing |
|
Flexible DOM-style XML manipulation for dynamic attributes on |
CLI framework |
Cobra |
Battle-tested (kubectl, gh, terraform), built-in help generation, shell completion |
File watching |
|
Cross-platform file system events, no polling overhead |
4.2. Top-Level Decomposition
Bausteinsicht follows a pipes-and-filters architecture with a clear separation between data formats:
-
Model package reads/writes the JSON model. Knows nothing about draw.io XML.
-
DrawIO package reads/writes mxGraph XML. Knows nothing about the JSON model.
-
Sync Engine orchestrates the bidirectional sync between both formats, using a state file for three-way merge.
-
CLI layer is a thin shell delegating to these packages.
This decomposition ensures that model format changes (e.g., switching from JSONC to YAML) or draw.io format changes do not ripple across the codebase.
4.3. Quality Goal Strategies
| Quality Goal | Strategy |
|---|---|
Learnability |
|
IDE Support |
JSON Schema is published and referenced via |
LLM Friendliness |
All CLI commands support |
4.4. Key Design Patterns
4.4.1. Three-Way Merge for Bidirectional Sync
Rather than simple overwrite, Bausteinsicht uses a .bausteinsicht-sync state file to detect which side changed. This prevents data loss when both the model and draw.io are edited between syncs. See Sync Specification for details.
4.4.2. Template-Based Styling
Visual styles are not hardcoded. A draw.io template file defines the appearance of each element kind via bausteinsicht_template attributes. Users can customize templates without modifying source code.
4.4.3. Thin CLI, Rich Library
The cmd/ layer contains only argument parsing and output formatting. All logic lives in internal/ packages that can be tested independently and reused (e.g., for a future LSP server or web API).
5. Building Block View
5.1. Level 1: Overall System
5.1.1. Motivation
At the centre sits a small core engine — model (the JSONC world), drawio (the XML world), and sync as the mediator between them, so changes to one format never leak into the other package. Around this core, a set of feature packages add analysis, evolution, interop, and styling, and a thin CLI layer (cmd/bausteinsicht) wires commands to them. A second binary (cmd/bausteinsicht-lsp) reuses the same library code to serve editors over the Language Server Protocol.
In total the system is two binaries over ~23 production packages (plus two test-only helper packages). The complete map is in the Package Map below; the core engine is decomposed further in the Level 2 sections.
5.1.2. Building Blocks (core)
| Element | Kind | Technology | Description |
|---|---|---|---|
CLI |
container |
Go / Cobra |
Command-line interface providing validate, sync, export, and watch commands |
draw.io |
container |
Go / etree |
draw.io XML document handling — elements, connectors, templates, and labels |
Export |
container |
Go |
Diagram export to PNG and SVG formats via draw.io CLI |
Model |
container |
Go |
JSONC architecture model management — loading, validation, and mutation |
Sync Engine |
container |
Go |
Bidirectional synchronization engine between JSONC model and draw.io diagrams |
Watcher |
container |
Go / fsnotify |
File system monitoring for automatic sync on model or diagram changes |
Developer |
actor |
Developer using the Bausteinsicht CLI to manage architecture models |
|
draw.io App |
external_system |
draw.io desktop or web application for visual diagram editing |
|
IDE |
external_system |
IDE with JSON Schema support for JSONC editing with autocompletion |
5.1.3. Important Interfaces
| Interface | Description |
|---|---|
|
Parses a JSONC file into the Go model struct. Validates against schema constraints. |
|
Writes the model back to JSONC, preserving comments where possible. |
|
Parses a draw.io XML file into a manipulable document structure. |
|
Writes the document back to uncompressed draw.io XML. |
|
Reads a template file and extracts styles keyed by |
|
Executes one full bidirectional sync cycle — a pure function with no I/O. |
|
Reads the |
|
Writes the updated sync state after successful sync. |
5.1.4. Package Map (all building blocks)
Every production package, grouped by role. Each row is a Level-1 building block: its responsibility and its source location. The core three (model, drawio, sync) are decomposed further in the Level 2 sections below.
| Package | Responsibility | Source |
|---|---|---|
|
DSL types, JSONC loader (comment-preserving patch + full save), validation, ID/wildcard resolution |
|
|
Read/write draw.io XML: document, element, connector, template, HTML label |
|
|
Bidirectional sync: diff, forward/reverse apply, conflict resolution, state, layout, badges, metadata |
|
|
fsnotify file watcher driving |
|
| Package | Responsibility | Source |
|---|---|---|
|
Evaluate architectural rules from the model ( |
|
|
Relationship-graph analysis: cycles, dependency depth, centrality ( |
|
|
Architecture health score across weighted categories ( |
|
|
Detect unused/forgotten elements, using git history ( |
|
|
Full-text search over elements, relationships, views ( |
|
|
Group, merge, and validate multiple models together as one workspace ( |
|
| Package | Responsibility | Source |
|---|---|---|
|
Versioned model captures, semantically diffable ( |
|
|
As-is vs. to-be model comparison ( |
|
|
Architecture changelog between two git points ( |
|
| Package | Responsibility | Source |
|---|---|---|
|
Hierarchical auto-layout engine ( |
|
|
Generate a draw.io template from the specification ( |
|
|
Apply/remove metric heatmap overlays on diagrams ( |
|
| Package | Responsibility | Source |
|---|---|---|
|
Import models from Structurizr DSL and LikeC4 ( |
|
|
Export the model as Structurizr DSL |
|
|
Render views as text diagrams: C4-PlantUML, Mermaid, DOT, D2, HTML, sequence ( |
|
|
Render element attributes as AsciiDoc/Markdown tables ( |
|
|
Export diagram pages to PNG/SVG via headless draw.io ( |
|
|
Generate the JSON Schema from the Go model types ( |
|
| Package | Responsibility | Source |
|---|---|---|
|
Language Server Protocol server: diagnostics, code lenses; backs the |
|
|
Note
|
internal/chaos (fault-injection helper) and internal/benchmarks are test-only utilities, not production building blocks.
|
5.2. Level 2: Sync Engine
The sync engine is the most complex package. It decomposes into five sub-components.
5.2.1. Building Blocks
| Element | Kind | Technology | Description |
|---|---|---|---|
CLI |
container |
Go / Cobra |
Command-line interface providing validate, sync, export, and watch commands |
draw.io |
container |
Go / etree |
draw.io XML document handling — elements, connectors, templates, and labels |
Model |
container |
Go |
JSONC architecture model management — loading, validation, and mutation |
Conflict |
component |
Go |
Conflict detection and resolution when both model and diagram changed |
Diff |
component |
Go |
Three-way change detection comparing model, diagram, and last-sync state |
Engine |
component |
Go |
Main sync orchestrator coordinating diff, forward, reverse, and conflict resolution |
Forward Sync |
component |
Go |
Applies model changes to draw.io diagrams (model → drawio) |
Reverse Sync |
component |
Go |
Applies draw.io changes back to the JSONC model (drawio → model) |
State |
component |
Go |
Sync state persistence using SHA256 hashes for change detection |
Watcher |
container |
Go / fsnotify |
File system monitoring for automatic sync on model or diagram changes |
5.3. Level 2: DrawIO Package
The draw.io package handles all mxGraph XML concerns.
5.3.1. Building Blocks
| Element | Kind | Technology | Description |
|---|---|---|---|
Connector |
component |
Go |
Connector/relationship CRUD for edges between elements |
Document |
component |
Go |
XML parsing, page management, and file I/O for .drawio files |
Element |
component |
Go |
Element CRUD operations on mxGraphModel objects and mxCells |
Label |
component |
Go |
HTML label generation and parsing for element display text |
Template |
component |
Go |
Template loading and style lookup from .drawio template files |
Export |
container |
Go |
Diagram export to PNG and SVG formats via draw.io CLI |
Sync Engine |
container |
Go |
Bidirectional synchronization engine between JSONC model and draw.io diagrams |
draw.io App |
external_system |
draw.io desktop or web application for visual diagram editing |
5.4. Level 2: Model Package
5.4.1. Building Blocks
| Element | Kind | Technology | Description |
|---|---|---|---|
CLI |
container |
Go / Cobra |
Command-line interface providing validate, sync, export, and watch commands |
Loader |
component |
Go |
JSONC read/write with comment stripping and trailing comma handling |
Patch |
component |
Go |
Comment-preserving JSONC mutations for reverse sync |
Resolve |
component |
Go |
Dot-notation element resolution and wildcard pattern matching |
Types |
component |
Go |
Core struct definitions for elements, relationships, views, and specification |
Validate |
component |
Go |
Model validation rules for elements, relationships, and views |
Sync Engine |
container |
Go |
Bidirectional synchronization engine between JSONC model and draw.io diagrams |
IDE |
external_system |
IDE with JSON Schema support for JSONC editing with autocompletion |
5.5. Go Package Layout
The directory structure maps directly to the building blocks above (packages, not every file):
bausteinsicht/ ├── cmd/ │ ├── bausteinsicht/ // main CLI — one file per command (init, sync, add, repl, export*, …) │ └── bausteinsicht-lsp/ // Language Server Protocol binary ├── internal/ │ ├── model/ drawio/ sync/ watcher/ // core engine │ ├── constraints/ graph/ health/ stale/ search/ // analysis │ ├── snapshot/ diff/ changelog/ // evolution │ ├── layout/ template/ overlay/ // layout & styling │ ├── importer/ exporter/ diagram/ table/ export/ schema/ // interop & export │ ├── lsp/ // IDE integration │ └── chaos/ benchmarks/ // test-only helpers ├── go.mod └── go.sum
5.5.1. Conventions
-
cmd/bausteinsicht/contains only CLI wiring — no business logic; all logic lives ininternal/packages (so the LSP binary can reuse it). -
internal/ensures packages cannot be imported by external code. -
Each package has a clear single responsibility (see the Package Map).
-
Cross-package dependencies flow inward toward the core:
cmd → feature packages → sync → model + drawio. -
The
modelanddrawiopackages never depend on each other —syncis the only mediator. -
chaosandbenchmarksare imported only from_test.gofiles, so production code never depends on them.
6. Runtime View
6.1. Sync Command
The most important runtime scenario. Shows what happens when the user runs bausteinsicht sync.
6.1.1. Key Observations
-
The sync engine receives all data as parameters — it performs no I/O itself
-
Forward sync runs before reverse sync in the same cycle
-
The state file is only written after both directions complete successfully
-
If any step fails, no files are written (atomic operation)
-
Forward sync adds metadata (title, source, author, timestamp) and a legend to each view page
6.2. Watch Mode
Shows the continuous sync loop triggered by file changes.
6.2.1. Debounce Strategy
File editors often trigger multiple save events in rapid succession (write + metadata update). The watcher debounces events with a 300ms window: after the first change event, it waits 300ms for additional events before triggering a sync.
6.3. Init Command
6.4. LLM-Driven Modification
Shows how an LLM agent uses CLI commands to modify the architecture.
6.4.1. Design Decision: Separate Add and Sync
The add commands only modify the JSON model. They do not trigger a sync automatically. This keeps commands predictable (single responsibility) and allows LLMs to batch multiple model changes before syncing.
6.5. Import from Structurizr / LikeC4
Shows how an existing model is brought into Bausteinsicht (bausteinsicht import <file> --from structurizr|likec4).
The result is immediately syncable with bausteinsicht sync. The importer never touches draw.io — it only produces the model.
6.6. REPL Save: Patch vs. Full-Save Fallback
The REPL keeps the model in memory and, on save, chooses how to persist so JSONC comments survive when possible. This is the key data-integrity flow.
6.6.1. Key Observations
-
Validation runs before any write — an invalid model is never persisted.
-
Pure additions take the comment-preserving patch path; modifications/deletions conservatively fall back to a full rewrite with a visible warning about comment loss.
-
The same patch primitives back the non-interactive
addcommands.
6.7. Stale Detection
Shows how bausteinsicht stale flags forgotten elements using git history.
When run outside a git repository the git lookups degrade gracefully (no date), and detection falls back to the view/relationship membership checks.
6.8. Error & Recovery: Failed Write During Sync
The contract requires at least one error/recovery scenario. This shows why a failed or interrupted sync never corrupts the working files.
6.8.1. Key Observations
-
The engine computes everything in memory first; files are written only afterwards, each via a temp-file-plus-rename so a partial write cannot truncate a real file.
-
The state file is written last. If any earlier write fails, the state is left untouched, so the next run re-detects the same change and retries — the operation is effectively idempotent and self-healing.
-
The state file carries a SHA-256 checksum, so external tampering or truncation is detected on load rather than silently trusted.
7. Deployment View
7.1. Infrastructure Level 1
Bausteinsicht is a single-binary CLI tool that runs on the developer’s local machine. There is no server, no database, and no network communication at runtime.
7.1.1. Motivation
Bausteinsicht intentionally has the simplest possible deployment: one binary, no dependencies. This directly serves the Learnability quality goal — download, run, be productive.
7.1.2. Quality and Performance Features
| Feature | Description |
|---|---|
Startup time |
< 10ms (native Go binary, no runtime to load) |
Sync time |
< 100ms for models with up to 200 elements (in-memory XML/JSON processing) |
Binary size |
~10-15 MB (single statically linked binary) |
Zero dependencies |
No runtime, no package manager, no database, no network |
7.1.3. Mapping of Building Blocks to Infrastructure
| Building Block | Deployment |
|---|---|
|
The compiled binary, distributed via GitHub Releases for Linux, macOS, Windows (amd64 + arm64) |
|
Compiled into the same binary. No separate deployment. |
JSON Schema |
Published to GitHub (raw URL) and referenced via |
Template |
Bundled as a Go embed resource in the binary for |
7.2. Distribution
7.2.1. GitHub Releases
Each tagged version produces compressed archives for all target platforms via GoReleaser. Archives are named bausteinsicht_v2.6.7_{Os}_{Arch} — .tar.gz for Linux and macOS, .zip for Windows:
| Platform | Archive |
|---|---|
Linux amd64 |
|
Linux arm64 |
|
Linux arm (v7) |
|
macOS amd64 |
|
macOS arm64 (Apple Silicon) |
|
Windows amd64 |
|
Windows arm64 |
|
The authoritative build matrix is .goreleaser.yml: goos: [linux, darwin, windows] × goarch: [amd64, arm64, arm] (with goarm: 7). GoReleaser produces one archive per combination Go actually supports, skipping the rest (e.g. darwin/arm); the table above lists the primary targets and is not exhaustive. Each archive also ships with an SBOM (SPDX-JSON and CycloneDX-JSON); a checksums.txt covers all artifacts.
7.2.2. Installation
# Pick the archive for your platform from the releases page: # https://github.com/docToolchain/Bausteinsicht/releases/latest # then download, unpack, and install (example: Linux amd64, release v1.2.3) curl -L https://github.com/docToolchain/Bausteinsicht/releases/download/v1.2.3/bausteinsicht_1.2.3_linux_amd64.tar.gz -o bausteinsicht.tar.gz tar -xzf bausteinsicht.tar.gz sudo install -m 0755 bausteinsicht /usr/local/bin/bausteinsicht # Or via Go install go install github.com/docToolchain/Bausteinsicht/cmd/bausteinsicht@latest
7.2.3. Embedded Resources
The init command needs a default template and sample model. These are embedded into the binary using Go’s embed package:
//go:embed templates/default.drawio
var defaultTemplate []byte
//go:embed templates/sample-model.jsonc
var sampleModel []byte
This eliminates the need to distribute additional files alongside the binary.
8. Cross-cutting Concepts
8.1. Error Handling
8.1.1. Strategy
Bausteinsicht uses Go’s explicit error handling pattern consistently. Errors propagate upward and are only formatted for the user at the CLI layer.
| Layer | Error Handling |
|---|---|
|
Return |
|
Catch errors, format them for the user (plain text or JSON depending on |
8.1.2. Exit Codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Validation error or sync conflict |
2 |
File not found or I/O error |
8.1.3. Structured Error Output
With --format json, errors are returned as:
{
"error": "validation failed",
"details": [
{ "path": "model.webshop.api", "message": "unknown kind: container2" }
]
}
This enables LLM agents to parse and react to errors programmatically.
8.2. Testing Strategy
8.2.1. Test Levels
| Level | Scope | Approach |
|---|---|---|
Unit tests |
Individual functions (label parsing, ID resolution, XML element creation) |
Table-driven tests using |
Integration tests |
Package-level (full model load/save cycle, full XML round-trip) |
Use test fixtures in |
End-to-end tests |
Full CLI commands |
Run the compiled binary with test input files. Verify output files and exit codes. Use |
8.2.2. Test Fixtures
Each package has a testdata/ directory with representative input files:
internal/model/testdata/
├── valid-model.jsonc
├── invalid-missing-kind.jsonc
└── nested-elements.jsonc
internal/drawio/testdata/
├── simple-diagram.drawio
├── multi-page.drawio
└── with-containers.drawio
internal/sync/testdata/
├── scenario-new-element/
│ ├── before-model.jsonc
│ ├── before-drawio.drawio
│ ├── sync-state.json
│ ├── expected-model.jsonc
│ └── expected-drawio.drawio
└── scenario-conflict/
└── ...
8.2.3. Golden File Testing
For sync scenarios, the test runs a sync cycle and compares the output against expected files. If the output changes intentionally, update the golden files with a flag:
go test ./internal/sync/ -update-golden
8.3. Logging and Output
8.3.1. User-Facing Output
Normal output goes to stdout in one of two formats:
-
text (default): Human-readable summaries
-
json (
--format json): Machine-parseable output for LLM integration
Warnings and errors go to stderr.
8.3.2. Verbose Mode
With --verbose, additional details are printed to stderr:
[DEBUG] Loading model from architecture.jsonc [DEBUG] Loading draw.io from architecture.drawio [DEBUG] Loading sync state from .bausteinsicht-sync [DEBUG] Detected 2 model changes, 1 drawio change, 0 conflicts [DEBUG] Forward: creating element webshop.cache [DEBUG] Forward: updating label on webshop.api [DEBUG] Reverse: updating title of customer [INFO] Sync complete: 2 forward changes, 1 reverse change
8.3.3. No Logging Framework
Bausteinsicht uses Go’s standard log package for verbose output and fmt for normal output. No external logging framework is needed for a CLI tool.
8.4. Version Management
The binary version is set at build time via ldflags:
go build -ldflags "-X main.version=0.1.0" ./cmd/bausteinsicht
The --version flag prints:
bausteinsicht version 0.1.0
GoReleaser handles this automatically for tagged releases.
8.5. JSONC Handling
Standard Go encoding/json does not support comments. Bausteinsicht strips comments before parsing:
-
Remove single-line comments (
// …) -
Remove trailing commas before
}and] -
Parse the cleaned JSON with
encoding/json -
When writing back, format with
json.MarshalIndent(comments are lost on write)
|
Note
|
Preserving user comments across sync cycles is a future enhancement. For v1, comments in the model file are stripped on write. |
8.6. File Atomicity
When writing files, Bausteinsicht uses a write-to-temp-then-rename pattern:
-
Write to a temporary file in the same directory (e.g.,
.model.jsonc.tmp) -
os.Renamethe temp file to the target path (atomic on most filesystems) -
On failure, the original file remains intact
This prevents corrupted files if the process is interrupted during write.
8.7. Configuration Discovery
Bausteinsicht uses convention over configuration:
-
Look for
*.jsoncin the current directory. Exactly one match is used as the model file; if several exist, the command aborts and requires--model(it never silently picks one, since the tool mutates and synchronizes files). -
Look for
*.drawioin the current directory (the diagram file) -
Look for
.bausteinsicht-syncin the current directory -
Template:
template.drawioin the current directory, or fall back to the embedded default
All paths can be overridden via CLI flags (--model, --template).
9. Architecture Decisions
Architecture decisions are documented as ADRs (Architecture Decision Records) following the Nygard format with a weighted Pugh matrix for option evaluation.
All ADRs are located in src/docs/arc42/ADRs/.
| ADR | Title | Status | Summary |
|---|---|---|---|
Choice of DSL Format |
Accepted |
JSON with JSON Schema (JSONC) chosen over TypeScript DSL and Custom DSL (Langium). Scored highest on learnability, IDE support, LLM friendliness, and bidirectional sync suitability. |
|
Choice of Implementation Language |
Accepted |
Go chosen over Python and Kotlin/JVM. Single-binary distribution, LLM-friendly code generation, compile-time type safety, and dedicated mxGraph library support. |
|
Risk Classification |
Accepted |
Tier 2 (Extended Assurance) — determined by Code Type = 2 (Business Logic). Existing toolchain covers most mitigations. Guides AI-assisted development quality requirements. |
|
Sequence Diagram Export |
Superseded by ADR-008 |
Originally rejected |
|
Auto-Layout Engine for New Diagram Pages |
Accepted |
Layered/grid/none placement modes for fresh diagram pages. Relationship-aware BFS ordering within scopes; manual positions preserved on incremental sync. |
|
CLI Add Command Strategy |
Accepted |
|
|
Testing Strategy |
Accepted |
Unit / integration / E2E classification, plus property-based tests (pgregory.net/rapid). Tests trace to issues and use cases. |
|
Sequence Diagram Export (Revisited) |
Accepted |
Reverses ADR-004: implements |
|
Drill-Down Navigation |
Accepted |
Page-based drill-down (one draw.io page per view + cross-page links + back button) over single-page semantic zoom. Uses native draw.io pages; keeps bidirectional sync simple. |
10. Quality Requirements
10.1. Quality Requirements Overview
| Category (ISO 25010) | Quality Requirement |
|---|---|
Usability / Learnability |
The tool and its DSL must be easy to learn for developers and architects. No prior knowledge of C4, Structurizr, or LikeC4 should be required. |
Usability / Operability (IDE Support) |
Editing the architecture model must be supported by IDE features (autocompletion, validation, hover docs) without installing tool-specific plugins. |
Compatibility / Interoperability (LLM Friendliness) |
The model format and CLI must be designed so that LLM agents can reliably read, write, and manipulate architecture models. |
Functional Suitability / Correctness (Sync Reliability) |
Bidirectional synchronization must never silently lose data. New elements, changed descriptions, and relationships must be correctly propagated in both directions. |
Portability / Installability |
The tool must be distributable as a single binary without runtime dependencies. Installation must not require package managers, virtual environments, or runtimes. |
Maintainability / Modifiability (Flexible Hierarchy) |
The element hierarchy must not be hardcoded to a fixed number of levels. Users must be able to define their own element kinds and nesting depth. |
10.2. Quality Scenarios
10.2.1. QS-1: Learnability
Context |
A developer unfamiliar with Bausteinsicht wants to create an architecture model for a new project. |
Stimulus |
The developer reads the getting-started documentation and opens their IDE. |
Acceptance Criteria |
Within 30 minutes, the developer has created a valid architecture model with at least 3 elements, 2 relationships, and 1 draw.io view — using only the documentation and IDE autocompletion. |
Driven by: ADR-001 (JSON chosen for universal familiarity and IDE support).
10.2.2. QS-2: IDE Support
Context |
An architect edits a |
Stimulus |
The architect types a new element definition. |
Acceptance Criteria |
The editor provides autocompletion for property names, validates values against allowed types, and shows hover documentation for each field — without any Bausteinsicht-specific plugin installed. Only the JSON Schema association is required. |
Driven by: ADR-001 (JSON Schema provides free IDE support via SchemaStore).
10.2.3. QS-3: LLM Friendliness
Context |
An LLM agent (e.g., Claude) is asked to add a new microservice to an existing architecture model. |
Stimulus |
The agent reads the current model file and uses CLI commands. |
Acceptance Criteria |
The agent successfully adds the element with title, description, technology, and relationships, then runs |
Driven by: ADR-001 (JSON is the native output format of LLMs).
10.2.4. QS-4: Sync Reliability
Context |
An architect modifies an element’s description in draw.io and adds a new element in the JSON model simultaneously. |
Stimulus |
|
Acceptance Criteria |
The description change from draw.io is applied to the model. The new element from the model appears in the draw.io view. No data is silently lost. If a true conflict exists (same field changed in both), a warning is displayed. |
10.2.5. QS-5: Installability
Context |
A developer on macOS, Linux, or Windows wants to use Bausteinsicht. |
Stimulus |
The developer downloads the binary and runs |
Acceptance Criteria |
The tool runs immediately without installing Go, Python, Node.js, Java, or any other runtime. Total time from download to first successful command is under 1 minute. |
Driven by: ADR-002 (Go chosen for single-binary distribution).
10.2.6. QS-6: Flexible Hierarchy
Context |
An architect models a system where a component contains sub-components, which in turn contain further sub-components (3+ nesting levels beyond the C4 standard). |
Stimulus |
The architect defines deeply nested elements in the JSON model. |
Acceptance Criteria |
The model validates successfully. draw.io views correctly render all nesting levels. There is no artificial limit on hierarchy depth. |
11. Risks and Technical Debts
This is the project’s living risk register. Its risk IDs (R-n) and debt IDs (D-n) are local to this chapter. The ATAM Architecture Review (2026-03-06) was the original input and remains the point-in-time analysis with its own separate numbering (sensitivity/tradeoff points); do not assume R-5 here equals R-5 there.
11.1. Risks
Priority is derived from likelihood × impact. The table is ordered by priority, resolved items last.
| ID | Risk | Likelihood | Impact | Priority | Mitigation / Status |
|---|---|---|---|---|---|
R-3 |
draw.io XML format changes — the format is not formally versioned; a breaking change could break the sync engine. |
Medium |
High |
High |
Custom |
R-6 |
DSL import parsing errors — Structurizr DSL / LikeC4 parsers may fail on valid-but-unexpected syntax, losing data on import. |
Medium |
High |
High |
Comprehensive error messages + validation warnings; add roundtrip tests (import → export → import). Open. Burdens |
R-5 |
LSP server crashes or hangs — editor integration depends on its availability. |
Medium |
Medium |
Medium |
Health checks, restart logic, timeout handling. Open. Burdens |
R-9 |
Headless draw.io dependency — PNG/SVG export drives the draw.io Electron app via xvfb + dbus; a fragile setup that fails silently in unusual environments. |
Medium |
Medium |
Medium |
Text exports ( |
R-10 |
Auto-layout quality — the engine is a layered heuristic, not crossing-minimization; dense diagrams still need manual cleanup. |
Medium |
Low |
Low |
|
R-4 |
Large-model sync performance — O(n·m) element/view matching on every run. |
Low |
Medium |
Low |
Go keeps this acceptable below ~1000 elements; profile with a 500-element model. Open (v2). |
R-7 |
Large-model export performance — exporting 1000+ elements to text/DSL could be slow. |
Low |
Medium |
Low |
Streaming export for large models; profile at 500 elements. Open (v2). |
R-8 |
Search completeness — pattern matching might miss results due to escaping or scope filtering. |
Low |
Low |
Low |
Property-based tests for search patterns; edge cases (special chars, deep nesting). Open. Burdens |
R-11 |
Supply chain (SEC-014) — the Dockerfile downloads install scripts without checksum verification. |
Low |
Medium |
Low |
Deferred; see the security review. Affects the dev/CI image only, not the released binary. |
R-1 |
Path traversal via |
— |
— |
Resolved |
|
R-2 |
Sync-state file corruption — a corrupt |
— |
— |
Resolved |
The state file carries a SHA-256 checksum verified on load ( |
11.2. Technical Debts
Each item names the building block it burdens (see Chapter 5).
-
D-1 — Auto-layout is heuristic (
internal/layout,internal/sync): layered/grid placement, not edge-crossing minimization. Adequate for typical C4 diagrams; a force-directed engine remains a future option (ADR-005). (Replaces the former "no auto-layout" debt — auto-layout now ships.) -
D-2 — REPL full-save loses JSONC comments (
cmd/bausteinsichtrepl,internal/modelpatch): pure additions are patched comment-preserving, but any modification/deletion falls back to a full rewrite that drops comments (with a visible warning). Tracked in #410. -
D-3 — LSP editor support (
internal/lsp): the server works but only the VS Code integration is complete; extend to Neovim/Sublime/Emacs. -
D-4 — Import/export roundtrip is lossy (
internal/importer,internal/exporter): Structurizr/LikeC4 import and Structurizr export may drop custom metadata on a full cycle; document lossy fields, plan full roundtrip for v2. -
D-5 — Coverage gate friction (CI): the SonarCloud new-code-coverage gate (80%) blocks refactors touching untested legacy code while the baseline is ~70%; see #449.
11.3. Non-Risks
The following were evaluated and confirmed not to be threats (see ATAM Non-Risks):
-
XXE / XML-bomb — the draw.io XML parser does not expand external entities.
-
JSON deserialization — Go’s
encoding/jsonis memory-safe. -
Command injection from input — the tool does invoke external binaries (
internal/export→ draw.io,internal/stale&internal/changelog→git,internal/lspre-exec), but they are resolved fromPATHand their names/arguments are never derived from model or template content, so untrusted input cannot inject a command. HardenPATHin restricted environments. -
Supply chain — the product binary uses no npm and only 4 direct Go modules (
beevik/etree,fsnotify,cobra,pgregory.net/rapid), with module verification; the optional VS Code extension is a separate TypeScript/npm artifact, not part of the CLI. -
Regression safety — 863 test functions (9 skipped), property-based tests (
pgregory.net/rapid), and pre-commit hooks guard the build.
12. Glossary
The terms below form Bausteinsicht’s ubiquitous language — the same words are used in this documentation, the specification, and the source code.
12.1. Model concepts
| Term | Definition |
|---|---|
Element |
A node in the architecture model — a system, container, component, actor, datastore, and so on. Elements are nested arbitrarily deep and keyed by a dot-separated path that doubles as their unique ID (e.g. |
Element kind |
The type of an element ( |
Relationship |
A directed connection between two elements, with a |
View |
A named filter over the model that becomes one draw.io page. A view lists what to |
Scope |
The element a view drills into. A scoped view shows that element’s children inside its boundary and powers page-based drill-down navigation. |
Dynamic view |
A behavioral view: an ordered list of interaction |
Specification |
The model section that defines the vocabulary: which element kinds and relationship kinds exist, plus tags, patterns, and decision records. |
Lifecycle status |
An optional element field tracking its stage: |
Pattern |
A reusable element/relationship template that |
12.2. Synchronization
| Term | Definition |
|---|---|
Forward sync |
Applying model changes to the draw.io diagram (model → draw.io). |
Reverse sync |
Applying draw.io edits (titles, descriptions, technology, new shapes/connectors) back to the model (draw.io → model). |
Model-wins |
The conflict-resolution policy: when the same element changed on both sides since the last sync, the model value is kept and a warning is shown — never a silent overwrite. |
Sync state |
The checksummed JSON snapshot of the last synchronized state, stored in |
|
The attribute carried by every synced draw.io cell that anchors it to its model element — the link between the two file formats. |
Endpoint lifting |
When a relationship’s endpoint is not directly visible in a view, the connector is attached to the nearest visible ancestor element instead. |
Boundary |
A container shape in draw.io that visually encloses a scoped element’s children; it auto-expands to fit them. |
Drill-down navigation |
Page-based navigation between abstraction levels: one draw.io page per view, with sync-generated cross-page links and a back button. See ADR-009. |
12.3. Tooling & styling
| Term | Definition |
|---|---|
Template |
A draw.io file whose shapes carry a |
Overlay |
A metric heatmap applied on top of a diagram (e.g. coloring elements by a metric), added/removed by the |
Badge |
A small visual marker attached to an element in the diagram (e.g. a decision badge). |
Snapshot |
A versioned capture of the whole architecture model, stored under |
Stale element |
An element flagged by the |
Workspace |
A multi-model grouping that lets several Bausteinsicht models be validated and related together. |
REPL |
The interactive shell ( |
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.