Dieser Post setzt den vierten Teil fort. In den vorigen Teilen hatte jedes Beispiel-Modell ein oder zwei Views. Hier geht es darum, wie Bausteinsicht C4-ähnliche Multi-Level-Views aus einem einzigen Modell erzeugt.

Views als Linsen auf das Modell

Das Modell in Bausteinsicht beschreibt das vollständige System — alle Elemente, alle Beziehungen, alle Hierarchieebenen. Eine View ist eine Linse, die ausschneidet was in einem bestimmten Kontext relevant ist.

Das bedeutet: dieselben Elemente können in mehreren Views erscheinen. Der model-Abschnitt in architecture.jsonc bleibt eine Quelle der Wahrheit — die Views bestimmen nur die Darstellung.

Das C4-Muster mit Bausteinsicht

Das C4-Modell (Context → Container → Component → Code) lässt sich direkt auf Bausteinsicht-Views abbilden:

{
  "specification": {
    "elements": {
      "actor":     { "notation": "Actor" },
      "system":    { "notation": "Software System", "container": true },
      "container": { "notation": "Container",        "container": true },
      "component": { "notation": "Component",        "container": true },
      "database":  { "notation": "Database" }
    }
  },

  "model": {
    "customer": { "kind": "actor",   "title": "Customer" },
    "admin":    { "kind": "actor",   "title": "Admin" },
    "shop": {
      "kind": "system", "title": "Online Shop",
      "children": {
        "frontend": { "kind": "container", "title": "Web Frontend",  "technology": "React" },
        "api": {
          "kind": "container", "title": "REST API", "technology": "Go",
          "children": {
            "orderhandler":   { "kind": "component", "title": "Order Handler" },
            "producthandler": { "kind": "component", "title": "Product Handler" },
            "authhandler":    { "kind": "component", "title": "Auth Handler" }
          }
        },
        "db":  { "kind": "database",   "title": "PostgreSQL",   "technology": "PostgreSQL" },
        "cache": { "kind": "database", "title": "Redis Cache",  "technology": "Redis" }
      }
    },
    "payment": { "kind": "system", "title": "Payment Provider (extern)" }
  },

  "relationships": [
    { "from": "customer",        "to": "shop",              "label": "kauft ein",      "kind": "uses" },
    { "from": "admin",           "to": "shop",              "label": "verwaltet",       "kind": "uses" },
    { "from": "shop.frontend",   "to": "shop.api",          "label": "REST calls",      "kind": "uses" },
    { "from": "shop.api",        "to": "shop.db",           "label": "reads/writes",    "kind": "uses" },
    { "from": "shop.api",        "to": "shop.cache",        "label": "cache lookup",    "kind": "reads" },
    { "from": "shop.api",        "to": "payment",           "label": "charge()",        "kind": "uses" },
    { "from": "shop.api.orderhandler",   "to": "shop.db",   "label": "persist orders",  "kind": "uses" },
    { "from": "shop.api.producthandler", "to": "shop.db",   "label": "read products",   "kind": "reads" },
    { "from": "shop.api.authhandler",    "to": "shop.cache","label": "validate token",  "kind": "reads" }
  ],

  "views": {
    "context": {
      "title":   "System Context",
      "include": ["customer", "admin", "shop", "payment"]
    },
    "containers": {
      "title":   "Container View",
      "scope":   "shop",
      "include": ["customer", "admin", "shop.*", "payment"]
    },
    "api-components": {
      "title":   "API Components",
      "scope":   "shop.api",
      "include": ["shop.api.*", "shop.db", "shop.cache"]
    }
  }
}

Das ergibt drei Tabs in architecture.drawio: System Context, Container View, API Components.

scope: Die Bounding Box

Das scope-Feld in einer View ist der Schlüssel zu strukturierten Diagrammen.

Wenn eine View einen scope definiert, wird das Scope-Element als Bounding Box (Swimlane / Container-Rahmen) gerendert. Alle anderen Elemente der View erscheinen entweder innerhalb dieser Box (wenn sie Kinder des Scope-Elements sind) oder außerhalb (externe Akteure und Systeme).

Beispiel: Container View mit scope = "shop"

┌─────────────────────────────────────────────────────┐
│  Online Shop                                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────┐  │
│  │ Web Frontend │→ │   REST API   │→ │PostgreSQL│  │
│  │    React     │  │      Go      │  │          │  │
│  └──────────────┘  └──────────────┘  └──────────┘  │
│                           ↓                         │
│                    ┌──────────────┐                 │
│                    │  Redis Cache │                 │
│                    └──────────────┘                 │
└─────────────────────────────────────────────────────┘
         ↑ customer, admin (außerhalb der Box)
                              ↗ payment (außerhalb)

Die Bounding Box visualisiert die Systemgrenze auf einen Blick.

include: Welche Elemente erscheinen?

include akzeptiert exakte IDs und Wildcards:

PatternBedeutung

"shop"

Exakt das Element shop

"shop.*"

Alle direkten Kinder von shop (frontend, api, db, cache) — aber nicht deren Kinder

"shop.**"

Alle Kinder und Kindeskinder von shop (rekursiv)

"shop.api.*"

Alle Komponenten innerhalb von shop.api

filter-tags und exclude-tags

Views lassen sich auch nach Tags filtern:

"views": {
  "external-systems": {
    "title":       "Externe Systeme",
    "filter-tags": ["external"],
    "include":     ["customer", "shop", "payment"]
  },
  "active-only": {
    "title":        "Aktive Komponenten",
    "include":      ["shop.**"],
    "exclude-tags": ["deprecated", "archived"]
  }
}

Wenn ein Element als scope einer View verwendet wird, setzt Bausteinsicht automatisch einen Drill-Down-Link auf dieses Element in allen anderen Views.

In der Context View ist shop ein normales Rechteck. Da shop aber der Scope der Container View ist, bekommt das Rechteck in der Context View einen klickbaren Link — ein Klick öffnet direkt die Container View.

Dasselbe gilt für shop.api als Scope der API-Components-View: ein Klick auf die API-Box in der Container View öffnet die Components-View.

Das ergibt eine navigierbare Zoom-Hierarchie:

System Context → [click "Online Shop"] → Container View → [click "REST API"] → API Components

Kein manuelles Setzen von Links — Bausteinsicht leitet das vollständig aus der scope-Konfiguration ab.

Rücknavigation: Zurück zur übergeordneten View

Views mit scope bekommen automatisch einen Back-Button in der oberen linken Ecke des Diagramms. Der Button navigiert zur übergeordneten View — der View, aus der man per Drill-Down hierher navigiert ist.

Das macht die Navigation bidirektional: rein- und rauszoomen ohne manuelles Tab-Wechseln.

Praktisches Beispiel: Drei Views generieren

Mit dem Modell aus dem Abschnitt oben:

bausteinsicht sync

Ausgabe:

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

architecture.drawio öffnen — drei Tabs:

  1. System Context: Customer, Admin, Online Shop (ein Rechteck mit Drill-Down-Link), Payment Provider

  2. Container View: Scope-Box "Online Shop" mit Frontend, REST API, PostgreSQL, Redis Cache darin; Customer, Admin und Payment außerhalb

  3. API Components: Scope-Box "REST API" mit den drei Handlern darin; PostgreSQL und Redis Cache außerhalb

Die drei Views teilen sich dasselbe Modell. Eine Beziehung im Modell ändert sich — ein sync aktualisiert alle drei Tabs gleichzeitig.

Unsichtbare Elemente: Eine nützliche Warnung

Wenn ein Element im Modell existiert, aber in keiner View auftaucht, gibt bausteinsicht sync eine Warnung aus:

WARNING: Element "shop.api.orderhandler" exists in the model but is not visible in any view
         — add it to a view's include list

Das ist kein Fehler — das Element bleibt im Modell und kann jederzeit einer View hinzugefügt werden. Die Warnung verhindert, dass Elemente im Modell „verschwinden" ohne es zu bemerken.

Mit bausteinsicht status bekommt man eine Übersicht aller Elemente und ob sie in einer View sichtbar sind.

Beispiel-Modell

Das Beispiel für diesen Teil ist als ausführbare JSONC-Datei unter teil_5.jsonc abgelegt mit allen drei Views (context, containers, api-components).

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

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

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

api-components
containers
context

Generierte PlantUML-Diagramme via bausteinsicht export-diagram — hier die Container View:

Diagram
Diagram
Diagram

Was als nächstes kommt

Offizielle Dokumentation: User Manual · Tutorial auf doctoolchain.org