Dieser Post setzt den dreißigsten Teil fort. Bis jetzt waren Bausteinsicht-Diagramme reine Visualisierung — man sieht ein System, aber es gibt keinen Weg hinein. Mit dem link-Feld auf Elementen (docToolchain/Bausteinsicht#483) ändert sich das: jedes Element kann einen Link bekommen, der beim draw.io-Export als klickbares link-Attribut auf dem Shape landet.

Das macht ein Diagramm zu einem echten Einstiegspunkt in die Doku-Landschaft — Sprung zu einer ADR, einem Jira-Ticket oder einer Confluence-Seite, direkt aus dem Shape heraus.

Nicht zu verwechseln mit der ADR-Registry aus Teil 11 (specification.decisions, navigierbar über bausteinsicht adr list/show). Das link-Feld hier ist einfacher: ein rohes URL-Feld direkt auf dem Element, ohne eigene Registry — für alles, was extern verlinkt werden soll, nicht nur strukturierte ADRs.

Jedes Element kann optional ein link-Feld bekommen:

"api": {
  "kind": "container", "title": "REST API", "technology": "Go",
  "link": "examples/teil_31_adr.html"
}

Beim nächsten bausteinsicht sync landet dieser Wert als link-Attribut auf dem <object> im draw.io-XML — im Diagramm ist das Shape dann direkt anklickbar, öffnet den Link im Browser.

Eine ADR als eigenständige Seite

Statt auf eine externe URL zu verlinken, zeigt dieses Beispiel etwas Griffigeres: eine selbst geschriebene Architecture Decision Record, die mit Asciidoctor zu HTML gebaut und direkt in diesem Tutorial-Post eingebettet wird.

Die ADR beschreibt genau die Regel, die weiter unten demonstriert wird — warum Drill-Down-Links Vorrang vor user-definierten Links haben:

asciidoctor examples/teil_31_adr.adoc -o examples/teil_31_adr.html

Die Content-Pipeline (infra/builder/build-adr-html.sh) übernimmt das automatisch bei jedem Build — analog zum bestehenden draw.io-Sync-Schritt. Jede examples/*_adr.adoc-Datei wird zu HTML gebaut und als statisches Asset ausgeliefert.

Eingebettet sieht das so aus:

Kein Seitenwechsel nötig — die verlinkte ADR ist direkt hier lesbar, genau wie sie auch beim Klick auf das api-Shape im Diagramm erscheinen würde.

Was passiert, wenn ein Element gleichzeitig Scope einer Detail-View ist (bekommt automatisch einen Drill-Down-Link) und einen user-definierten link trägt? Beide könnten dasselbe Shape beanspruchen.

SituationErgebnis

Nur user-definierter link

Link bleibt erhalten, zeigt auf die angegebene URL

Nur Drill-Down (Element ist scope einer View)

Automatischer Link zur Detail-View (data:page/id,view-<viewID>)

Beide gleichzeitig

Drill-Down gewinnt — der user-definierte Link wird verworfen, sync warnt

Die Begründung steht in der eingebetteten ADR oben — kurz zusammengefasst: Drill-Down ist Navigation innerhalb der Diagramm-Serie und wird als grundlegender erwartet als ein Sprung nach außen.

Das Beispielmodell

teil_31.jsonc modelliert einen kleinen Online Shop mit vier Elementen, die vier unterschiedliche Link-Use-Cases zeigen:

  • onlineshop.gatewaylink auf ein externes GitHub-Issue (docToolchain/Bausteinsicht#483, das Ticket, das dieses Feature eingeführt hat). Kein Konflikt, der Link bleibt im Export erhalten.

  • onlineshop.apilink auf eine ganze Seite, examples/teil_31_adr.html. Kein Konflikt, der Link bleibt im Export erhalten.

  • onlineshop.payment — ist scope der payment-details-View und hat einen link. Hier greift die Priority-Regel: der Drill-Down-Link gewinnt, sync gibt eine Warnung aus.

  • onlineshop.dblink mit Anchor auf eine absolute URL, https://paul-fleischmann.com/projekte/bausteinsicht/tutorial/examples/teil_31_adr.html#_modellelement_database_postgresql, springt beim Klick direkt zum Abschnitt "Modellelement: Database (PostgreSQL)" statt an den Seitenanfang.

{
  // Teil 31: Klickbare Links in exportierten Diagrammen — Online Shop Beispiel
  "specification": {
    "elements": {
      "actor":     { "notation": "Actor" },
      "system":    { "notation": "Software System", "container": true },
      "container": { "notation": "Container",        "container": true }
    },
    "relationships": {
      "uses": { "notation": "uses" }
    }
  },
  "model": {
    "customer": { "kind": "actor", "title": "Customer" },
    "onlineshop": {
      "kind": "system", "title": "Online Shop",
      "children": {
        // Use Case: externer Ticket-Link — verweist auf das GitHub-Issue,
        // das dieses Feature eingeführt hat. Kein Konflikt, der Link bleibt
        // im Export erhalten.
        "gateway": {
          "kind": "container", "title": "API Gateway", "technology": "Go",
          "link": "https://github.com/docToolchain/bausteinsicht/issues/483"
        },
        // Klarer Fall: user-definierter Link auf eine ganze Seite, kein
        // Konflikt — dieses Element ist nirgends der `scope` einer View,
        // der Link bleibt also im draw.io-Export erhalten wie modelliert.
        "api": {
          "kind": "container", "title": "REST API", "technology": "Go",
          "link": "examples/teil_31_adr.html"
        },
        // Priority-Demo: dieses Element ist gleichzeitig `scope` der
        // "payment-details"-View UND hat einen user-definierten Link.
        // Der Drill-Down-Link gewinnt (ADR-009) — sync gibt eine Warnung aus.
        "payment": {
          "kind": "container", "title": "Payment Service", "technology": "Go",
          "link": "examples/teil_31_adr.html#_entscheidung"
        },
        // Use Case: Deep-Link mit Anchor — verweist nicht nur auf die Seite,
        // sondern direkt auf den Abschnitt, der diese Komponente beschreibt.
        // Anders als beim `api`-Element ist der Link hier absolut (volle
        // Produktions-URL): beim Inline-SVG-Export (siehe unten) überlebt
        // nur eine absolute URL unverändert — ein relativer Link würde gegen
        // den internen Pfad der draw.io-CLI aufgelöst und liefe ins Leere.
        "db": {
          "kind": "container", "title": "Database", "technology": "PostgreSQL",
          "link": "https://paul-fleischmann.com/projekte/bausteinsicht/tutorial/examples/teil_31_adr.html#_modellelement_database_postgresql"
        }
      }
    }
  },
  "relationships": [
    { "from": "customer",              "to": "onlineshop.gateway", "label": "kauft ein",            "kind": "uses" },
    { "from": "onlineshop.gateway",     "to": "onlineshop.api",     "label": "routet zu",             "kind": "uses" },
    { "from": "onlineshop.api",         "to": "onlineshop.payment", "label": "delegiert Zahlung",     "kind": "uses" },
    { "from": "onlineshop.payment",     "to": "onlineshop.db",      "label": "schreibt Transaktion",  "kind": "uses" }
  ],
  "views": {
    "context":    { "title": "System Context", "include": ["customer", "onlineshop"] },
    "containers": { "title": "Container View", "scope": "onlineshop", "include": ["customer", "onlineshop.*"] },
    "payment-details": {
      "title": "Payment Service — Details",
      "scope": "onlineshop.payment",
      "include": ["onlineshop.api", "onlineshop.payment", "onlineshop.db"]
    }
  }
}

bausteinsicht sync ausführen

bausteinsicht sync --model teil_31.jsonc

Die Ausgabe zeigt die Priority-Warnung für onlineshop.payment:

WARNING: element onlineshop.payment: user-defined link overridden by drill-down link (ADR-009)
Forward (model → draw.io): 14 added, 0 updated, 0 deleted

Im resultierenden draw.io-XML zeigt sich der Unterschied zwischen allen vier Fällen direkt:

<!-- onlineshop.gateway — externer Ticket-Link bleibt erhalten -->
<object label="API Gateway" link="https://github.com/docToolchain/bausteinsicht/issues/483" ...>

<!-- onlineshop.api — user-definierter Seiten-Link bleibt erhalten -->
<object label="REST API" link="examples/teil_31_adr.html" ...>

<!-- onlineshop.payment — Drill-Down gewinnt, user-Link wurde verworfen -->
<object label="Payment Service" link="data:page/id,view-payment-details" ...>

<!-- onlineshop.db — Deep-Link mit Anchor, absolute URL bleibt erhalten -->
<object label="Database" link="https://paul-fleischmann.com/projekte/bausteinsicht/tutorial/examples/teil_31_adr.html#_modellelement_database_postgresql" ...>

Ergebnis im Diagramm

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

Im draw.io-Viewer sind alle vier Shapes anklickbar, aber mit unterschiedlichem Ziel:

  • API Gateway öffnet das GitHub-Issue in einem neuen Tab.

  • REST API öffnet die eingebettete ADR — genau die Seite, die weiter oben schon zu sehen war.

  • Payment Service springt stattdessen zur payment-details-View — die Drill-Down-Navigation bleibt intakt, obwohl auch dort ein link-Feld im Modell steht.

  • Database öffnet dieselbe ADR wie REST API, aber direkt am Abschnitt, der die Database-Komponente beschreibt, statt oben auf der Seite.

Generierte PNG-Dateien:

containers
context
payment-details

Generierte PlantUML-Diagramme:

Diagram
Diagram
Diagram

Der interaktive viewer.diagrams.net-iframe oben lädt die rohe .drawio-XML zur Laufzeit im Browser nach — das funktioniert nur, wenn die Datei öffentlich unter der angegebenen URL erreichbar ist. Es gibt eine Alternative, die ganz ohne externen Dienst und Netzwerk-Roundtrip auskommt: bausteinsicht export --image-format svg nutzt die echte draw.io-CLI (nicht Kroki) und erhält dabei link-Attribute als echte <a xlink:href>-Elemente im SVG. Direkt als Inline-Markup ins HTML eingebettet — nicht als <img src="…​svg">, das würde die Links verschlucken — sind einzelne Shapes darin wirklich klickbar, ganz ohne JavaScript oder externen Dienst.

Die Content-Pipeline (infra/builder/generate-drawio-svg-inline.sh) erzeugt das bei jedem Build:

bausteinsicht export --image-format svg --output out/

Für die Container View sieht das eingebettete Ergebnis so aus:

Online Shop
REST API
[Go]
Database
[PostgreSQL]
API Gateway
[Go]
Payment Service
[Go]
Customer
kauft ein
routet zu
delegiert Zahlung
schreibt Transaktion
← System Context
Container View

Source: site/content/projekte/bausteinsicht/tutorial/examples/teil_31.jsonc
Last synced: 2026-08-10 03:49
Generated by Bausteinsicht
Legend
Actor
Container
Software System
Text is not SVG - cannot display

Zwei Einschränkungen gegenüber dem interaktiven Viewer:

  • Nur absolute URLs funktionieren — auch mit Anchor. API Gateway (https://github.com/…​;) ist korrekt klickbar, ebenso Database (https://paul-fleischmann.com/…​teil_31_adr.html#_modellelement_database_postgresql) — der Klick landet direkt im Abschnitt, der die Database-Komponente beschreibt. REST API hat dagegen einen relativen Link (examples/teil_31_adr.html, ohne Domain) — die draw.io-CLI löst den beim Export gegen ihr eigenes Installationsverzeichnis auf, nicht gegen die Seite, in die das SVG später eingebettet wird:

    <a xlink:href="file:///opt/drawio/resources/app.asar/drawio/src/main/webapp/examples/teil_31_adr.html">

    Im Browser läuft dieser Klick ins Leere. Damit relative Links hier funktionieren, müssten sie im Modell als absolute URL modelliert werden — mit oder ohne Anchor, das spielt keine Rolle, solange die Domain mit dabei ist.

  • Drill-Down-Links gehen komplett verloren. Payment Service hat im interaktiven Viewer einen klickbaren Sprung zur payment-details-View (data:page/id,…​). Dieses interne URL-Schema ist im Browser nicht navigierbar — die draw.io-CLI lässt das Shape beim SVG-Export deshalb ganz ohne <a>-Wrapper. Mehrseitige Navigation zwischen Views funktioniert nur im interaktiven iframe.

Der Ansatz lohnt sich also nur, wenn alle relevanten Links absolute URLs sind und keine Drill-Down-Navigation gebraucht wird — z. B. für ein einzelnes, in sich geschlossenes Diagramm mit externen Ticket- oder Doku-Links.

SituationEmpfehlungBeispiel oben

Element hat eine zugehörige ADR

link auf die gebaute ADR-HTML-Seite

onlineshop.api

Element ist Gegenstand eines offenen Tickets

link direkt auf das Jira-/GitHub-Issue

onlineshop.gateway

Nur ein bestimmter Abschnitt einer Seite ist relevant

link mit Anchor (#abschnitt-id) statt auf die ganze Seite

onlineshop.db

Element hat ausführliche Doku in Confluence

link auf die Confluence-Seite — funktioniert genauso wie der GitHub-Issue-Link, nur mit anderer URL

Element ist scope einer Detail-View

Kein link setzen — Drill-Down gewinnt ohnehin (ADR-009)

onlineshop.payment

Was als nächstes kommt

Damit schließt die Bausteinsicht-Tutorial-Serie ein weiteres Kapitel ab: von reiner Visualisierung (Teil 1) bis zu Diagrammen, die direkt in die restliche Doku-Landschaft verlinken.

Die wichtigsten Einstiegspunkte zum Nachschlagen:

Offizielle Dokumentation: User Manual · Tutorial auf doctoolchain.org