Dieser Post setzt den dritten Teil fort. Das Alleinstellungsmerkmal von Bausteinsicht ist die bidirektionale Synchronisation: Änderungen im JSONC-Modell landen im draw.io-Diagramm — und Änderungen im Diagramm landen im Modell. Wie das funktioniert, erklärt dieser Post.

Das Problem: Zwei Repräsentationen, eine Wahrheit

Architekturdiagramme haben ein fundamentales Problem: entweder ist der Code die Quelle der Wahrheit und das Diagramm wird automatisch generiert (gut für Konsistenz, schlecht für visuelle Kontrolle), oder das Diagramm ist die Quelle und wird manuell gepflegt (gut für Aussehen, schlecht für Konsistenz).

Bausteinsicht löst das durch echte Bidirektionalität: Modell und Diagramm sind gleichwertig. Der Sync-Algorithmus erkennt, welche Seite sich geändert hat, und überträgt die Änderung zur anderen Seite.

Die .bausteinsicht-sync State-Datei

Die Grundlage des Sync-Algorithmus ist die .bausteinsicht-sync-Datei. Sie enthält einen Snapshot des Zustands nach dem letzten erfolgreichen Sync:

{
  "timestamp": "2025-06-11T09:00:00Z",
  "model_hash": "sha256:a1b2c3...",
  "drawio_hash": "sha256:d4e5f6...",
  "elements": {
    "onlineshop.frontend": {
      "title": "Web Frontend",
      "technology": "React",
      "kind": "container"
    }
  },
  "relationships": [
    { "from": "customer", "to": "onlineshop", "label": "uses", "kind": "uses" }
  ],
  "rendered_elements": {
    "onlineshop.frontend": true,
    "onlineshop.api": true
  }
}

Diese State-Datei ist der Dreiwegs-Vergleich: sync vergleicht den aktuellen Zustand von Modell und Diagramm jeweils mit dem letzten bekannten Zustand — und erkennt dadurch auf welcher Seite sich etwas verändert hat.

Die .bausteinsicht-sync-Datei gehört ins Git-Repository neben architecture.jsonc. Sie ist der einzige Weg, wie Bausteinsicht Konflikte von legitimen Änderungen unterscheidet.

Der Sync-Zyklus

bausteinsicht sync führt jeden Lauf als atomaren Fünf-Schritte-Zyklus aus:

1. DetectChanges   → ChangeSet (was hat sich auf welcher Seite geändert?)
2. ResolveConflicts → conflicting fields aufgelöst (Modell gewinnt)
3. ApplyForward    → Modell-Änderungen → draw.io übertragen
4. ApplyReverse    → draw.io-Änderungen → Modell übertragen
5. SaveState       → .bausteinsicht-sync aktualisieren

Der gesamte Zyklus ist eine pure Funktion ohne Seiteneffekte — alle I/O findet davor und danach statt. Das macht den Algorithmus testbar und deterministisch.

Forward-Sync: Modell → draw.io

Der Forward-Sync übernimmt alle Modelländerungen ins Diagramm.

Neue Elemente

Neue Elemente erscheinen mit einem roten gestrichelten Rahmen als visueller Marker:

strokeColor=#FF0000;dashed=1;

Der rote Rahmen signalisiert: „Dieses Element wurde gerade hinzugefügt — positioniere es." Sobald das Element manuell verschoben wurde und sync erneut läuft, verschwindet der rote Rahmen.

Positionen werden beibehalten

Bausteinsicht merkt sich die gesetzten Positionen im Diagramm. Bei jedem Forward-Sync werden nur Titel, Beschreibung, Technologie und Stile aktualisiert — nicht die Position. Manuell arrangierte Layouts bleiben erhalten.

Tag-Stile werden angewendet

Wenn ein Element Tags hat, die in specification.tags mit style-Attributen definiert sind, werden diese Stile auf das draw.io-Element angewendet:

"specification": {
  "tags": [
    { "id": "external", "style": { "fillColor": "#f5f5f5", "fontColor": "#666666" } }
  ]
},
"model": {
  "paymentprovider": {
    "kind": "system",
    "title": "Payment Provider",
    "tags": ["external"]
  }
}

Status-Badges

Elements mit status-Feld erhalten automatisch einen farbigen Badge im Diagramm:

StatusFarbeBedeutung

proposed

Gelb

Geplant

design

Blau

Im Entwurf

implementation

Orange

In Entwicklung

deployed

Grün

Produktiv

deprecated

Rot

Veraltet

archived

Grau

Eingestellt

Reverse-Sync: draw.io → Modell

Der Reverse-Sync überträgt Änderungen aus dem Diagramm zurück ins JSONC-Modell.

Was wird zurückgeschrieben?

Folgende Änderungen im Diagramm landen im Modell:

draw.io AktionEffekt im Modell

Element umbenennen (Doppelklick → neuer Titel)

title des Elements wird aktualisiert

Technologie-Label ändern

technology wird aktualisiert

Neues Element in eine Container-Box ziehen

Neues Element wird als children hinzugefügt

Element aus dem Diagramm löschen

Element wird aus dem Modell entfernt

Verbindung zwischen zwei Elementen ziehen

Neue Relationship wird im relationships-Array angelegt

Verbindungspfeil umdrehen

from und to der Relationship werden getauscht, Metadaten bleiben erhalten

Verbindung löschen

Relationship wird aus dem Modell entfernt

Relationship Lifting

Ein wichtiges Konzept: Relationship Lifting. Wenn in draw.io eine Verbindung zwischen zwei Elementen gezogen wird, die in der Modell-Hierarchie auf verschiedenen Ebenen liegen, „hebt" Bausteinsicht die Verbindung automatisch auf die passende Hierarchieebene an.

Beispiel: Das Diagramm zeigt shop.frontend und shop.api (beide Container-Kinder von shop). Man zieht eine Verbindung von frontend zu api — genau diese Beziehung landet im Modell:

"relationships": [
  { "from": "shop.frontend", "to": "shop.api", "label": "API calls" }
]

Zieht man dagegen eine Verbindung von customer (Top-Level-Actor) zu shop.api (verschachtelter Container), wird sie korrekt als customer → shop.api angelegt — ohne manuelle Korrektur der IDs.

Konflikterkennung und -auflösung

Ein Konflikt entsteht wenn dasselbe Feld auf beiden Seiten gleichzeitig geändert wurde — also seit dem letzten sync.

Wann passiert das?

Typisches Szenario: Du änderst title im JSONC-Modell und jemand anderes ändert gleichzeitig denselben Titel direkt im draw.io-Diagramm — ohne vorher zu synchronisieren.

Auflösung: Modell gewinnt

Bausteinsicht verwendet eine einfache und vorhersehbare Strategie: das Modell gewinnt immer.

Conflict detected for element "shop.frontend":
  Field: title
  Model value:   "Web App"
  draw.io value: "Frontend Application"
  Last sync:     "Web Frontend"
  → Keeping model value. Edit draw.io manually if needed.

Der Konflikt wird als Warning ausgegeben — die Änderung im Modell wird übernommen, die draw.io-Änderung wird verworfen.

Diese Strategie ist bewusst einfach gehalten. Das JSONC-Modell ist die primäre Quelle der Wahrheit — draw.io ist ein View darauf.

Praktisches Beispiel: Vollständiger Sync-Zyklus

Ausgangssituation

architecture.jsonc und architecture.drawio sind synchron (letzter Sync vor 10 Minuten).

Schritt 1: Modell ändern

Im JSONC einen neuen Container hinzufügen:

"model": {
  "shop": {
    "kind": "system", "title": "Online Shop",
    "children": {
      "frontend": { "kind": "container", "title": "Web Frontend" },
      "api":      { "kind": "container", "title": "REST API" },
      "cache":    { "kind": "container", "title": "Redis Cache", "technology": "Redis" }
    }
  }
}

Schritt 2: Im Diagramm den API-Titel ändern

architecture.drawio öffnen, Doppelklick auf „REST API", umbenennen zu „REST API v2", speichern.

Schritt 3: Sync ausführen

bausteinsicht sync

Ausgabe:

Forward (model → draw.io):  1 added, 0 updated, 0 deleted
Reverse (draw.io → model):  0 added, 1 updated, 0 deleted

Ergebnis:

  • draw.io: Redis Cache erscheint mit rotem gestricheltem Rahmen in der Container View

  • architecture.jsonc: "title": "REST API v2" für den API-Container

Beide Seiten wurden in einem einzigen sync-Durchlauf aktualisiert.

Watch-Modus: Kontinuierliche Synchronisation

bausteinsicht watch überwacht beide Dateien mit einem Filesystem-Watcher und triggert den Sync-Zyklus automatisch bei jeder Änderung.

Das Resultat: Man kann gleichzeitig im Editor das Modell bearbeiten und in draw.io das Diagramm anpassen — der Watch-Prozess hält alles in Echtzeit synchron.

Im Watch-Modus empfiehlt sich git commit nach jedem abgeschlossenen Änderungsschritt. Der .bausteinsicht-sync State zeigt im git diff genau welche Elemente geändert wurden — ein nützliches Protokoll.

Beispiel-Modell

Das Beispiel für diesen Teil ist als ausführbare JSONC-Datei unter teil_4.jsonc abgelegt und zeigt den Zustand nach einem vollständigen Sync-Zyklus (inkl. Reverse-Sync: "REST API v2").

So sieht das Ergebnis in draw.io aus (bausteinsicht sync):

Das draw.io-File dafür findest du hier: teil_4.drawio

Generierte PNG-Dateien via bausteinsicht export --image-format png:

containers

Generierte PlantUML-Diagramme via bausteinsicht export-diagram:

Diagram

Was als nächstes kommt

Offizielle Dokumentation: User Manual · Tutorial auf doctoolchain.org