Dieser Post setzt den siebten Teil fort. Architekturprinzipien schreiben sich leicht in ADRs. Schwieriger ist es, sicherzustellen dass der Code — und die Architektur — diese Prinzipien über Monate und Teams hinweg tatsächlich einhält. Bausteinsicht löst das mit maschinenprüfbaren Constraints.

Was ist Architektur-Linting?

Linting kennt man aus der Entwicklung: ESLint prüft JavaScript-Code auf Stilregeln und Fehler, go vet prüft Go-Code auf Antipatterns. Architektur-Linting macht dasselbe für das Architekturmodell:

  • Keine direkte Datenbankverbindung aus der UI-Schicht

  • Alle Systeme brauchen eine Beschreibung

  • Maximale Verschachtelungstiefe von 3 Ebenen

  • Keine zirkulären Abhängigkeiten

  • Nur freigegebene Technologien

Diese Regeln sind im Modell als constraints definiert und werden mit bausteinsicht lint geprüft.

Constraints im Modell definieren

Constraints gehören in den constraints-Array in architecture.jsonc (→ Teil 3).

Jeder Constraint braucht id, description und rule — plus regelspezifische Felder.

no-relationship

Verhindert direkte Verbindungen zwischen bestimmten Element-Typen:

"constraints": [
  {
    "id":          "NO-UI-DB",
    "description": "UI-Komponenten dürfen nicht direkt auf die Datenbank zugreifen",
    "rule":        "no-relationship",
    "from-kind":   "component",
    "to-kind":     "database"
  }
]

allowed-relationship

Definiert positiv, welche Element-Typen sich mit einem bestimmten Typ verbinden dürfen:

{
  "id":          "DB-ACCESS-ONLY-SERVICES",
  "description": "Nur Services und Repositories dürfen auf Datenbanken zugreifen",
  "rule":        "allowed-relationship",
  "to-kind":     "database",
  "from-kinds":  ["service", "repository"]
}

required-field

Erzwingt dass ein Feld bei allen Elementen eines bestimmten Typs gesetzt ist:

{
  "id":           "SYSTEM-NEEDS-DESCRIPTION",
  "description":  "Alle Systeme müssen eine Beschreibung haben",
  "rule":         "required-field",
  "element-kind": "system",
  "field":        "description"
},
{
  "id":           "CONTAINER-NEEDS-TECHNOLOGY",
  "description":  "Alle Container müssen eine Technologie angeben",
  "rule":         "required-field",
  "element-kind": "container",
  "field":        "technology"
}

Unterstützte Felder: description, technology, title.

max-depth

Begrenzt die Verschachtelungstiefe des Modells:

{
  "id":          "MAX-NESTING-3",
  "description": "Maximale Verschachtelungstiefe ist 3 (System → Container → Component)",
  "rule":        "max-depth",
  "max":         3
}

no-circular-dependency

Erkennt Zyklen im Abhängigkeitsgraphen mittels Tiefensuche:

{
  "id":          "NO-CYCLES",
  "description": "Keine zirkulären Abhängigkeiten zwischen Elementen",
  "rule":        "no-circular-dependency"
}

technology-allowed

Erzwingt einen genehmigten Technologie-Stack:

{
  "id":           "APPROVED-BACKEND-STACK",
  "description":  "Backend-Container dürfen nur genehmigte Technologien verwenden",
  "rule":         "technology-allowed",
  "element-kind": "container",
  "technologies": ["Go", "Rust", "Python", "PostgreSQL", "Redis"]
}

bausteinsicht lint

bausteinsicht lint

Ausgabe bei Verstößen:

VIOLATION [NO-UI-DB]: UI-Komponenten dürfen nicht direkt auf die Datenbank zugreifen: component kind must not relate to database kind
  - shop.frontend → shop.db
VIOLATION [CONTAINER-NEEDS-TECHNOLOGY]: Alle Container müssen eine Technologie angeben: all container elements must have "technology" set
  - shop.legacy: missing technology
lint: 2 violation(s) found

Ausgabe wenn alle Regeln bestanden:

All constraints passed.

Exit-Code: 0 bei Erfolg, 1 bei Verstößen — damit direkt in CI verwendbar.

JSON-Ausgabe für CI-Auswertung

bausteinsicht lint --format json
{
  "passed": false,
  "total": 2,
  "violations": [
    {
      "constraintId": "NO-UI-DB",
      "message": "...",
      "elements": ["shop.frontend → shop.db"]
    }
  ]
}

bausteinsicht validate

validate ist leichtgewichtiger als lint — es prüft nur die strukturelle Korrektheit des Modells (Schema, Referenzen):

bausteinsicht validate

Prüft:

  • Alle kind-Werte in model sind in specification.elements definiert

  • Alle kind-Werte in relationships sind in specification.relationships definiert

  • Alle decisions-Referenzen in Elementen existieren in specification.decisions

  • Alle tags in Elementen sind in specification.tags definiert

  • container: true für Elemente mit children

validate läuft implizit vor jedem sync — ein invalid Model wird nicht synchronisiert.

bausteinsicht health

health bewertet die Architekturqualität über mehrere Dimensionen hinweg und gibt einen Score von 0–100 (Note A–F):

bausteinsicht health

Ausgabe:

Architecture Health Report
==========================

Overall Score: 74.5/100 [B]
Summary: Good architecture documentation with some gaps
Timestamp: 2025-06-11T14:30:22Z

Model Statistics
----------------
Elements:      15
Relationships: 11
Views:          3

Category Scores
---------------
Completeness:  85.0/100 (weight: 40%)
  Details: 13/15 elements have descriptions
Conformance:   90.0/100 (weight: 30%)
  Details: All constraints passed
Complexity:    60.0/100 (weight: 30%)
  Details: High relationship density detected

Findings
--------
Completeness (2 findings):
  [WARN] Missing descriptions
         shop.legacy, shop.legacy.api: description is empty

Complexity (1 finding):
  [INFO] High coupling
         shop.api has 7 outgoing relationships — consider splitting

Kurzansicht für CI-Dashboards:

bausteinsicht health --summary
# → Overall Score: 74.5/100 [B]

# Als JSON
bausteinsicht health --format json --summary

# Report in Datei schreiben
bausteinsicht health --output docs/health-report.txt

CI/CD Integration

lint und validate sind direkt für CI gemacht. Beispiel GitHub Actions:

- name: Validate architecture model
  run: bausteinsicht validate

- name: Lint architecture constraints
  run: bausteinsicht lint

- name: Architecture health check (informational)
  run: bausteinsicht health --summary
  continue-on-error: true  # health bricht nicht den Build, informiert nur
lint gibt Exit-Code 1 bei Verstößen zurück — der CI-Build schlägt fehl. Das ist bewusst so: Architekturregeln sollen genauso verbindlich sein wie Coding-Guidelines.

Vollständiges Constraint-Set für eine Schichtenarchitektur

"constraints": [
  {
    "id":          "NO-CYCLES",
    "description": "Keine zirkulären Abhängigkeiten",
    "rule":        "no-circular-dependency"
  },
  {
    "id":          "NO-UI-TO-DB",
    "description": "UI greift nicht direkt auf DB zu",
    "rule":        "no-relationship",
    "from-kind":   "frontend",
    "to-kind":     "database"
  },
  {
    "id":          "DB-ONLY-FROM-BACKEND",
    "description": "Nur Backend-Services greifen auf DB zu",
    "rule":        "allowed-relationship",
    "to-kind":     "database",
    "from-kinds":  ["service", "repository"]
  },
  {
    "id":          "MAX-DEPTH",
    "description": "Maximale Tiefe: System → Container → Component",
    "rule":        "max-depth",
    "max":         3
  },
  {
    "id":           "SYSTEM-DOCUMENTED",
    "description":  "Alle Systeme brauchen eine Beschreibung",
    "rule":         "required-field",
    "element-kind": "system",
    "field":        "description"
  },
  {
    "id":           "CONTAINER-TECH",
    "description":  "Alle Container deklarieren ihre Technologie",
    "rule":         "required-field",
    "element-kind": "container",
    "field":        "technology"
  },
  {
    "id":           "APPROVED-STACK",
    "description":  "Nur freigegebene Technologien",
    "rule":         "technology-allowed",
    "element-kind": "container",
    "technologies": ["Go", "TypeScript", "React", "PostgreSQL", "Redis", "Kafka"]
  }
]

Beispiel-Modell

Das Beispiel für diesen Teil mit Schichtenarchitektur und Constraints liegt unter teil_8.jsonc.

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

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

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

containers
context

Generierte PlantUML-Diagramme via bausteinsicht export-diagram:

Diagram
Diagram

Was als nächstes kommt

Offizielle Dokumentation: User Manual · Tutorial auf doctoolchain.org