Mermaid Klassendiagramm Editor
Ein Klassendiagramm zeigt Typen und ihre Beziehungen: was was enthält, was von was erbt, was von was abhängt. Nimm es, wenn die Struktur des Codes der Punkt ist — ein Domänenmodell, eine Schnittstelle, ein Vererbungsbaum. Willst du zeigen, was zur Laufzeit passiert statt wie die Typen zusammenpassen, nimm ein Sequenzdiagramm.
Ein Domänenmodell für Zahlungen
Drei Beziehungsarten in einem Diagramm: Komposition für Teile, die den Besitzer nicht überleben, Vererbung für die Zahlungsarten und eine gewöhnliche Assoziation mit Multiplizität. Die Beziehungspfeile tragen hier fast die ganze Bedeutung — die Klassenkästen sind beinahe nebensächlich.
classDiagram
class Bestellung {
+String nummer
+Status status
+Betrag summe()
+void positionHinzufuegen(Artikel a, int menge)
}
class Bestellposition {
+Artikel artikel
+int menge
+Betrag zwischensumme()
}
class Zahlungsart {
<<abstract>>
+autorisieren(Betrag b) bool
}
class Kreditkarte {
+String letzteVier
+autorisieren(Betrag b) bool
}
class Ueberweisung {
+String iban
+autorisieren(Betrag b) bool
}
Bestellung "1" *-- "1..*" Bestellposition : enthält
Bestellung --> Zahlungsart : bezahlt mit
Zahlungsart <|-- Kreditkarte
Zahlungsart <|-- UeberweisungDurchgearbeitete Beispiele
1. Eine Klasse
`+` ist öffentlich, `-` privat, `#` geschützt. Ein Mitglied mit Klammern wird als Methode gezeichnet, ohne Klammern als Feld.
classDiagram
class Benutzer {
+String email
-String passwortHash
+bool pruefen(String eingabe)
}2. Vererbung und Schnittstellen
`<|--` ist Vererbung, gelesen als „der rechte erweitert den linken". Die `<<interface>>`-Annotation ist eine Beschriftung und kein Verhalten, macht das Diagramm aber erst lesbar.
classDiagram
class Repository {
<<interface>>
+finden(String id) Entitaet
+speichern(Entitaet e) void
}
class PostgresRepository {
-Verbindung conn
+finden(String id) Entitaet
+speichern(Entitaet e) void
}
class SpeicherRepository {
-Map ablage
+finden(String id) Entitaet
+speichern(Entitaet e) void
}
Repository <|.. PostgresRepository
Repository <|.. SpeicherRepository3. Komposition gegen Aggregation
Der Unterschied ist die Lebensdauer. Eine gefüllte Raute (`*--`) heißt, das Teil stirbt mit dem Ganzen — lösch die Rechnung und ihre Positionen sind weg. Eine hohle Raute (`o--`) heißt, das Teil lebt eigenständig weiter.
classDiagram
class Rechnung {
+String nummer
}
class Rechnungsposition {
+String bezeichnung
}
class Kunde {
+String name
}
Rechnung "1" *-- "1..*" Rechnungsposition : besteht aus
Kunde "1" o-- "0..*" Rechnung : hat erhalten4. Generics
Tilden liefern Typparameter: `Repository~Benutzer~`. Verschachtelung geht auch, ist gelegentlich nötig und selten eine gute Idee.
classDiagram
class Repository~T~ {
+finden(String id) T
+alle() List~T~
}
class Zwischenspeicher~K, V~ {
+holen(K schluessel) V
+ablegen(K schluessel, V wert) void
}
class BenutzerRepository {
+findeNachEmail(String email) Benutzer
}
Repository~Benutzer~ <|-- BenutzerRepository5. Notizen und Richtung
`direction LR` ordnet das Diagramm von links nach rechts an, was einem Vererbungsbaum meist besser bekommt als die Voreinstellung. Eine Notiz ist der richtige Ort für die Einschränkung, die in keinen Klassenkasten passt.
classDiagram
direction LR
class Ereignisspeicher {
+anhaengen(Ereignis e) void
+abspielen(String stromId) List~Ereignis~
}
class Momentaufnahme {
+int version
+byte[] inhalt
}
Ereignisspeicher --> Momentaufnahme : schreibt alle 100 Ereignisse
note for Ereignisspeicher "Nur Anfügen. Ereignisse werden nie geändert oder gelöscht."Syntaxreferenz für Klassendiagramme
Die Beziehungspfeile sind der Teil, den man sich merken sollte — sie unterscheiden ein Klassendiagramm von einer Kästchen-und-Striche-Zeichnung, und sie lesen sich von rechts nach links, was regelmäßig für Verwirrung sorgt.
| Syntax | Bedeutung |
|---|---|
| classDiagram | Öffnet das Diagramm. Groß-/Kleinschreibung zählt. |
| class Name { ... } | Klasse mit Mitgliedern. Die schließende Klammer gehört auf eine eigene Zeile. |
| +mitglied | Öffentlich. |
| -mitglied | Privat. |
| #mitglied | Geschützt. |
| +methode(Typ arg) Rückgabe | Eine Methode — die Klammern machen sie dazu. |
| <<interface>> / <<abstract>> | Stereotyp, als erste Zeile innerhalb der Klasse geschrieben. |
| A <|-- B | Vererbung: B erweitert A. |
| A <|.. B | Realisierung: B implementiert die Schnittstelle A. |
| A *-- B | Komposition: B kann A nicht überleben. |
| A o-- B | Aggregation: B existiert auch ohne A. |
| A --> B | Gerichtete Assoziation. |
| A ..> B | Abhängigkeit — A benutzt B, hält es aber nicht. |
| A "1" --> "0..*" B : Text | Multiplizität an beiden Enden plus Beschriftung. |
| class Repo~T~ | Generischer Typparameter. |
| note for A "Text" | Notiz an einer Klasse. |
| direction LR | Layoutrichtung ändern. |
Fehler, die Klassendiagramme zerlegen
Nachgestellt mit Mermaid 11.12.2. Klassendiagramme sind nachsichtiger als die meisten Typen hier, deshalb rendern mehrere dieser Fälle anstandslos und liefern das falsche Bild.
Was du siehst
Parse error, endet mit: got 'EOF_IN_STRUCT'
Warum
Ein mit `{` geöffneter Klassenrumpf, der nie geschlossen wurde. Der Tokenname ist ausnahmsweise hilfreich: Die Datei endete, während der Parser noch in einer Klasse war.
Lösung
Die Klammer auf einer eigenen Zeile schließen.
classDiagram
class Bestellung {
+String nummerclassDiagram
class Bestellung {
+String nummer
}Was du siehst
Parse error, endet mit: got 'ANNOTATION_END'
Warum
Ein Sequenzdiagramm-Pfeil in einem Klassendiagramm. `->>` bedeutet hier nichts, und der Parser kommt weit genug hinein, um einen verwirrenden Tokennamen auszugeben.
Lösung
Eine Klassenbeziehung verwenden: `-->` für Assoziation, `<|--` für Vererbung, `*--` für Komposition.
classDiagram
Bestellung ->> KundeclassDiagram
Bestellung --> Kunde : gehört zuWas du siehst
No diagram type detected matching given configuration
Warum
Falsche Groß-/Kleinschreibung. `classdiagram` ist nicht `classDiagram`.
Lösung
Das D großschreiben.
classdiagram
class BestellungclassDiagram
class BestellungWas du siehst
Der Pfeil zeigt in die entgegengesetzte Richtung
Warum
Beziehungspfeile werden von der Pfeilspitze rückwärts gelesen. `A <|-- B` heißt, B erbt von A, nicht umgekehrt. Falsch herum geschrieben rendert es trotzdem — es behauptet nun nur, deine Basisklasse erbe von ihrer eigenen Unterklasse.
Lösung
Lies es als „das ferne Ende erweitert das angespitzte Ende". Die Oberklasse steht links von `<|--`.
classDiagram
Kreditkarte <|-- ZahlungsartclassDiagram
Zahlungsart <|-- KreditkarteWas du siehst
Ein Feld erscheint, wo du eine Methode erwartet hast
Warum
Nur die Klammern unterscheiden eine Methode von einem Feld. `+speichern` ist ein Feld namens speichern, `+speichern()` eine Methode. Beides ist gültig, also warnt nichts.
Lösung
Die Klammern ergänzen und dahinter den Rückgabetyp, wenn er sichtbar sein soll.
classDiagram
class Repo {
+speichern
+finden
}classDiagram
class Repo {
+speichern(Entitaet e) void
+finden(String id) Entitaet
}Was du siehst
Komposition und Aggregation sehen fast gleich aus und bedeuten das Gegenteil
Warum
`*--` und `o--` unterscheiden sich um ein Zeichen und kodieren einen echten inhaltlichen Unterschied: ob das Teil das Ganze überleben kann. Das falsche zu nehmen ergibt ein formal einwandfreies Diagramm, das über deine Domäne die Unwahrheit sagt.
Lösung
Gefüllte Raute `*--`, wenn das Löschen des Elternobjekts das Kind mitnimmt. Hohle `o--`, wenn nicht.
classDiagram
Bestellung o-- Bestellposition : enthältclassDiagram
Bestellung *-- Bestellposition : enthältHinweise zum Rendern
Gemessen an Mermaid 11.12.2, so wie diese Seite es einsetzt.
Klassendiagramme wachsen schneller in die Höhe als jeder andere Typ hier
Drei Klassen ergeben eine viewBox von etwa 94×610. Vierzig Klassen ergeben 108×6900 — rund 172 Pixel Höhe pro Klasse, das steilste Wachstum der sechs Typen auf dieser Seite. Ein Diagramm mit vierzig Klassen ist knapp siebentausend Pixel hoch und als einzelnes Bild unbrauchbar. `direction LR` hilft, aber ab etwa fünfzehn Klassen ist es ehrlicher, das Diagramm nach fachlichen Bereichen zu teilen.
Die Zahl der Mitglieder beeinflusst die Breite kaum
Die Breite bestimmt die längste einzelne Mitgliedssignatur, nicht wie viele Mitglieder es gibt. Eine Klasse mit zwanzig kurzen Feldern ist nicht breiter als eine mit dreien. Du kannst also bei Mitgliedern großzügig und bei Klassen sparsam sein — das Gegenteil dessen, was die meisten zuerst tun.
Beschriftungen sind HTML, deshalb rendert der PNG-Export neu
Klassenbeschriftungen stecken in einem SVG-`<foreignObject>`, das Browser nicht auf ein Canvas zeichnen. Der PNG-Export dieser Seite hat deshalb früher still versagt und eine SVG-Datei zurückgegeben; inzwischen rendert er das Diagramm zuerst mit reinen SVG-Textbeschriftungen neu. Das PNG ist korrekt und in voller Größe, die Typografie weicht minimal vom Bildschirm ab.
Generics verwenden Tilden, und das hat eine Nebenwirkung
`Repository~T~` gibt es, weil spitze Klammern mit dem HTML in Beschriftungen kollidieren würden. Es heißt aber auch, dass eine echte Tilde in einem Klassen- oder Mitgliedsnamen als Beginn eines Typparameters gelesen wird. Selten, aber wenn es passiert, ausgesprochen verwirrend.
Das Theme ändert Farben, niemals das Layout
Helles und dunkles Theme ergeben für dieselbe Quelle eine identische viewBox. Ein Klassenkasten kann beim Themewechsel also weder seine Größe ändern noch Mitglieder abschneiden.
Wann etwas anderes besser passt
Dokumentierst du eine Datenbank statt eines Typsystems, nimm ein ER-Diagramm. Der Unterschied zählt: Klassendiagramme modellieren Verhalten und Vererbung, die Tabellen nicht haben, und ER-Diagramme modellieren Schlüssel und Multiplizität sauber, was Klassendiagramme nur andeuten.
Besteht das Diagramm überwiegend aus Kästen mit `-->` dazwischen und ohne Mitglieder, zeichnest du ein Architekturbild und kein Klassendiagramm. Ein Flussdiagramm mit Subgraphen sieht besser aus und behauptet weniger.
Und wird die Klassenliste aus dem Code erzeugt, überlege, ob das Diagramm es nicht auch sollte. Ein handgepflegtes Klassendiagramm einer Codebasis, die sich wöchentlich ändert, ist binnen eines Monats falsch — und ein falsches Diagramm kostet mehr als gar keines.
Andere Diagrammtypen
Geschrieben von Dominik Malsch · Zuletzt aktualisiert: