UML-Sequenzdiagramme mit PlantUML erstellen

https://oer-informatik.de/uml-sequenzdiagramm-plantuml

tl/dr; (ca. 10 min Lesezeit): Mithilfe des Tools plantUML lassen sich codebasiert UML-Sequenzdiagramme erstellen. In diesem Artikel werden einige Tipps und Tricks zum Umgang mit plantUML vorgestellt.

PlantUML stellt einen einfach versionierbaren und änderbaren Weg zur Verfügung, mit OpenSource-Software UML-Diagramme zu erstellen. Diese kann auch direkt in die Dokumentationen der Repositories eingebunden werden (die Markdown-Engines von Gitlab, Github, Bitbucket & Co. unterstützen PlantUML nativ). Leider nimmt es die Referenzbeschreibung von PlantUML (plantuml.com/de/sequence-diagram) mit dem UML-Standard nicht allzu genau. Im Folgenden werden einige Tipps gegeben, wie sich mit PlantUML ansehnliche und relativ standardkonforme Sequenzdiagramme darstellen lassen.

Minimales Sequenzdiagramm mit PlantUML

PlantUML-Quelltexte beginnen mit @startuml und enden mit @enduml. Die Kommunikationspartner werden automatisch erkannt, wenn man mit einem Pfeil signalisiert, von wo nach wo eine Nachricht versendet werden soll. Nach einem Doppelpunkt kann der Nachrichteninhalt übermittelt werden:

Sender -> Empfänger: Nachricht

Solange der stilisierte Pfeil auf den Empfänger zeigt können die Teilnehmer auch vertauscht werden:

Eva <- Adam: Nochmal Hallo

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

@startuml
Adam -> Eva: Hallo
@enduml
Ein minimales Sequenzdiagramm
Ein minimales Sequenzdiagramm

Die folgenden Diagramme wurden mit Layout-Befehlen angepasst (Details dazu im Abschnitt “Aufhübschen”, siehe unten). Um die Diagramme UML-konform zu halten sind folgende Einstellungen im Kopf nötig:

@startuml
skinparam style strictuml
hide footbox
@enduml
  • hide footbox versteckt die (in der UML nicht vorgesehenen) unteren Bezeichnungsboxen der Lebenslinien,

  • skinparam style strictuml ersetzt die halboffenen Pfeilspitzen bei synchronen Nachrichten durch geschlossenen ausgefüllte - wie im Standard vorgesehen.

Kommunikationspartner darstellen mit Plantuml

Sofern die Kommunikationspartner (Lebenslinien) ein besonderes Symbol erhalten sollen (in UML nicht so vorgesehen), einen Namen mit Freizeichen bekommen (z.B. wie es z.B. bei objektinstanzname: Klassenname der Fall ist) oder eine bestimmte Reihenfolge sichergestellt werden soll, werden die Kommunikationspartner vorab deklariert. Die Deklaration wird wie folgt notiert:

SYMBOLTYP "NameImDiagramm" as ALIASNAME

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

participant frontend
participant ":Backend" as backend
actor "martin :User" as martin
database "db :Datenbank" as db
collections "liste :Collection" as liste
Unterschiedliche Symbole für Kommunikationspartner
Unterschiedliche Symbole für Kommunikationspartner

Synchrone Nachrichten und aktive Lebenslinien

Man unterscheidet aktive (als Rechteck gezeichnete) und passive (gestrichelte / als Linie gezeichnete) Lebenslinien. PlantUML löst dies durch die Befehle activate LEBENSLINIENNAME und deactivate LEBENSLINIENNAME. So lange eine Akteurin selbst etwas ausführt oder auf die Antwort einer anderen Teilnehmerin wartet ist sie aktiv.

Bei Nachrichten unterscheidet die UML zwischen synchronen und asynchronen Nachrichten. Bei Synchronen Nachrichten wartet die Absenderin darauf, dass die Empfängerin die Nachricht vollständig bearbeitet hat (z.B. die aufgerufene Methode beendet wurde). Es kann (muss aber nicht) eine Antwort auf eine synchrone Nachricht folgen (bei Methodenaufrufen: Rückgabewert). Notiert wird die Nachricht mit einem durchgezogenen Pfeil mit ausgefüllter Pfeilspitze:

Absender -> Empfänger: SynchroneNachricht

Die Antwort wird als gestrichelte Linie mit offener Pfeilspitze notiert, was in PlantUML folgendermaßen angegeben werden muss:

Absender <<-- Empfänger: AntwortAufSynchroneNachricht

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

activate starter

starter -> meinKonto: setName("Hannes")
activate meinKonto
deactivate meinKonto

starter -> meinKonto: getName()
activate meinKonto
starter <<-- meinKonto: getName(): "Hannes"
deactivate meinKonto

starter -> meinKonto: getKontoNr()
activate meinKonto
starter <<-- meinKonto: "123456"
deactivate meinKonto

deactivate starter
Synchrone Nachrichten
Synchrone Nachrichten

Asynchrone Nachrichten

Bei Parallelisierung sind Nachrichten i.d.R. asynchron: die Absenderin wartet nicht darauf, dass eine Nachricht bearbeitet wurde und geht unmittelbar zur nächsten Aktion über. In der UML werden diese Nachrichten mit einer offenen Pfeilspitze notiert.

Asynchrone Nachrichten haben per Definition keine Antwortnachricht. Reaktionen auf asynchrone Nachrichten werden als neue selbständige asynchrone Nachrichten notiert.

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

starter ->> meinThread1:start()
activate meinThread1
starter ->> meinThread2:start()
activate meinThread2
starter <<- meinThread2:Bearbeitung beendet
deactivate meinThread2
Asynchrone Nachricht
Asynchrone Nachricht

Nachrichten ohne Absender / Empfänger

Sofern der Absender / Empfänger einer Nachricht für die betreffende Modellierung nicht relevant ist (oder unbekannt ist) spricht man von gefundenen (FoundMessage) bzw. verlorenen Nachrichten (LostMessage). Sie starten bzw. enden nicht an Lebenslinien, sondern an einem ausgefüllten Punkt. Ausgefüllt lässt sich dieser leider mit PlantUML nicht darstellen. Befehlsweise kann diese Notation genutzt werden (nicht ausgefüllter Punkt):

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

'am besten geeignet, nur Ausfüllung fehlt
[o->> webserver: HttpGetRequest("www.beispiel.de")
webserver ->>o] : HttpResponse: "<html>...</html>"

'verloren nach / gefunden von links
[o->> webserver: HttpGetRequest("www.beispiel.de")
?<<-o webserver: HttpResponse: "<html>...</html>"

'verloren nach / gefunden von rechts
webserver o<<-? : Log aktiviert
webserver ->>o] : Zugriff loggen
Gefundene und verlorene Nachrichten
Gefundene und verlorene Nachrichten

Um zu signalsieren, dass die Nachrichten im Nichts landen, wird in PlantUML an Stelle eines fehlenden Kommunikationspartners entweder das Ende des Diagramms angegeben (“[” bzw “]”), oder einfach das Ende des Pfeils (dessen Länge vom Nachrichtentext abhängt (mit ?). In einigen Fällen werden die Kreise an den Pfeilenden nicht mehr komplett dargestellt, dann ist es ggf. erforderlich etwas mit dieser nicht ganz eingängigen Notation zu spielen. Beispiele, wie die Lösung aussehen kann finden sich oben.

Objekterzeugung / Erzeugungsaufruf

Objekterzeugungsaufrufe werden in der UML notiert wie Antworten auf synchrone Nachrichten: gestrichelte Linie und offene Pfeispitze. In plantUML wird dieser Antwort notiert (starter -->> meinKonto: new()) und ein create vorangestellt:

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

@startuml
participant "StarterKlasse" as starter
activate starter

participant "meinKonto :Konto" as meinKonto
create meinKonto
starter -->> meinKonto: new()

deactivate starter
@enduml
Objekterzeugungsnachrichten
Objekterzeugungsnachrichten

Objektdekonstruktion

Dekonstruktionsnachrichten sind normale (synchrone oder asynchrone) Nachrichten, die das aufgerufene Objekt zerstören (je nach Programmiersprache: den Dekonstruktor aufrufen, Referenz auf NULL legen…). Die Zerstörung wird notiert, in dem an das Ende der Lebenslinie ein “X” geschrieben wird. In PlantUML wird destroy meinKonto notiert:

Codebeispiel: ( online bearbeitbar )

starter -> meinKonto: kontoAufloesen()
activate meinKonto
deactivate meinKonto
destroy meinKonto
Objektzerstörung
Objektzerstörung

Selbstnachrichten

Nachrichten kann jeder Teilnehmer auch an sich selbst schicken (Methodenaufrufe eines Objekts auf sich selbst). Absenderin und Empfängerin sind dann identisch.

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

konto->konto : getKontostand()
activate konto
konto<<--konto : getKontostand():123,45
deactivate konto
Selbstaufruf
Selbstaufruf

Schleifen im Sequenzdiagramm

Schleifen werden im UML-Sequenzdiagramm über kombinierte Fragmente dargestellt. Im Fall einer Wiederholung über das Schlüsselwort loop in Kombination mit einem guard (im einfachsten Fall der Anzahl der Schleifendurchläufe oder einer Bedingung, bis zu deren zutreffen der Schleifenrumpf ausgeführt wird).

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

group loop (verzinsungsJahre)

konto -> konto:   guthaben = verzinse(guthaben, zinsatz)
activate konto
konto <<-- konto: guthaben = verzinse(): guthaben + zins
deactivate konto

end
Wiederholungsstruktur
Wiederholungsstruktur

Bedingte Anweisungen im Sequenzdiagramm

Auch bedingte Anweisungen werden als kombiniertes Fragment dargestellt. Der guard (die Bedingung) wird von PlantUML mit eckigen Klammern umgeben, so dass die Notation einfach wie folgt ist:

alt BEDINGUNG
else else
end

Das zweite else taucht als guard im Diagramm auf. Falls ein elseif-Block folgen soll würde else BEDINGUNG notiert.

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

alt bestandskunde(Kunde) == true

Kunde <<-- Bank: true

else else

Bank -> Schufa: istSolvent(Kunde)
activate Schufa
Bank <<-- Schufa : schufaErgebnis
deactivate Schufa
Kunde <<-- Bank: schufaErgebnis
deactivate Bank

end
Bedingte Anweisung
Bedingte Anweisung

Concurrency - Parallelisierung im Sequenzdiagramm

Wenn mehrere Interaktionen parallel verlaufen wird dies mit dem kombinierten Fragment par notiert.

par
INTERAKTION
else
PARALLELEINTERAKTION
end

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

activate Browser

par
Browser -> Webserver: lade("hintergrundbild.gif")
activate Webserver
Browser <<-- Webserver: lade("hintergrundbild.gif"):file
deactivate Webserver

else
Browser -> Webserver: lade("titelbild.gif")
activate Webserver
Browser <<-- Webserver: lade("titelbild.gif"):file
deactivate Webserver
end

deactivate Browser
Parallelität / Concurrency
Parallelität / Concurrency

Referenzen: Auslagern von Abschnitten in weitere Sequenzdiagramme

Zur Übersichtlichkeit können Sequenzdiagrammme aufgeteilt und ineinander referenziert werden. In PlantUML wird dies über das Schlüsselwort ref umgesetzt, wobei die zu überdeckenden Lebenslinien als kommagetrennte Liste angefügt werden.

ref over nameLebenslinie1, nameLebenslinie2 : nameDesReferenziertenSequenzdiagramms

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

activate client1
client1 -> cs:setState(someState)
activate cs

ref over co1, cs : changeState

client1 <<-- cs:void
deactivate cs
deactivate client1
Referenzen auf andere Diagramme
Referenzen auf andere Diagramme

Der referenzierte Bereich wird dann an anderer Stelle in einem Interaktionsrahmen als eigenes Diagramm angegeben. PlantUML nutzt hierfür das Schlüsselwort mainframe. Der UML-Standard erwartet, dass dem Namen des Interaktionsrahmens sd (für sequence diagram) vorangestellt wird. Beispiel:

mainframe sd nameDesDiagramms

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

mainframe sd changeState

participant "subject :Subject" as cs
participant "observer1 :ConcreteObserver" as co1

activate cs
|||
deactivate cs
Interaktionsrahmen
Interaktionsrahmen

Weitere kombinierte Fragementen

UML-Sequenzdiagramme sehen eine Reihe weiterer kombinierter Fragmente vor, die in Plantuml über die folgende Notation umgesetzt werden:

group opt
  client-> server:request()
end

Neben dem hier gewählten opt können beliebige ander Zeichenketten genannt werden (strict, assert, critical wären z.B. in der UML noch vorgesehen).

Zeitliche Zusicherungen

Sofern die Abfolge von Nachrichten an bestimmte zeitliche Zusicherungen gebunden ist, können dies im Diagramm angegeben werden. PlantUML nutzt dafür Label, die in geschweiften Klammern angegeben un den Nachrichten vorangestellt werden.

Zeitdauern zwischen Label können dann unter der jeweiligen Zusicherung, die als guard nach UML-Notation in geschweiften Klammer stehen muss, angegeben werden. Da dies nur mit der neuen PlantUML-Engine teoz dargestellt werden kann, muss diese derzeit noch vorneweg über den Befehl !pragma teoz true aktiviert werden:

!pragma teoz true
{ChangeStateStartLabel} client1 -> cs:setState(someState)
activate cs
{ChangeStateStopLabel} client1 <<-- cs:void
deactivate cs

{ChangeStateStartLabel} <-> {ChangeStateStopLabel} : {500ms}

Codebeispiel: ( online bearbeitbar / vollständiger Quelltext )

Zeitliche Zusicherungen
Zeitliche Zusicherungen

plantUML-Webservice zur Diagrammerzeugung nutzen

PlantUML bietet einen Webservice, der Diagramme unmittelbar online rendert.

  • Variante 1: Codierter Quelltext: PlantUML kodiert den Quelltext, um ihn so über den Webservice zugänglich (und bearbeitbar) zu machen. Die URL ist in diesem

    Fall wie folgt aufgebaut:

    http://www.plantuml.com/plantuml/AUSGABEFORMAT/CODIERTERQUELLTEXT

    wobei als AUSGABEFORMAT png, svg, eps, epstext und txt genutzt werden kann. Wird als AUSGABEFORMAT uml gewählt erhält man den bearbeitbaren Quelltext.

  • Editierbare Links lassen sich auch über den Service von planttext.com erstellen, sie nutzen die selbe Quelltextcodierung und URLs nach dem Muster: https://www.planttext.com/?text=CODIERTERQUELLTEXT Auch in den erzeugten PNG-Dateien wird der codierte Quelltext als Titel hinterlegt, so dass sie sich relativ einfach später weiterverarbeiten lassen.

  • Variante 2: Quelltext direkt online rendern: der PlantUML-Quelltext ist online als Resource verfügbar und soll gerendert ausgegeben werden. Hierzu muss eine URL nach dem Muster:

    http://www.plantuml.com/plantuml/proxy?fmt=AUSGABEFORMAT&src=https://URL_PLANTUMLSOURCE

    erstellt werden, wobei als AUSGABEFORMAT png, svg, eps, epstext und txt genutzt werden kann. Diese Funktion scheint derzeit deaktiviert zu sein oder nur Entwicklern vorbehalten.

plantUML-Formatierung: Aufhübschen von Sequenzdiagrammen

Über Skins bietet PlantUML die Möglichkeit die Diagramme in Schriftart, Farben und Aussehen individuell anzupassen. An drei Beispielen soll das hier kurz gezeigt werden, wie die Konfiguration mit skinparam das Aussehen der Sequenzdiagramme individualisieren kann.

Darstellung ohne zusätzliche skinparam-Befehle

Ein Auszug aus dem Observer-Pattern sieht ohne Anpassungen des Skins mit PlanUML folgendermaßen aus:

Beispiel: ( online bearbeitbar / vollständiger Quelltext )

Beispiel ohne Formatierung
Beispiel ohne Formatierung

Skin mit angepassten Farben und näher am UML-Standard

Im folgenden Beispiel (das für die Diagramme oben verwendet wurde) wurde kosmetisch nur Farben und Schriftart anpasst, aber zusätzlich auch die oben genannten Tipps verwendet, um das Diagramm UML-konformer erscheinen zu lassen: skinparam style strictuml für die Pfeile und hide footbox für die Enden der Lebenslinien.

Codebeispiel: ( online bearbeitbar )

@startuml
skinparam style strictuml
skinparam DefaultFontName "Lucida Sans Typewriter"

skinparam sequence {
    ArrowColor DarkSlateBlue
    ActorBorderColor DarkSlateBlue
    ActorBackgroundColor whitesmoke

    LifeLineBorderColor DarkSlateBlue
    LifeLineBackgroundColor whitesmoke

    ParticipantBorderColor DarkSlateBlue
    ParticipantBackgroundColor whitesmoke
}

skinparam Note{
    BorderColor DarkSlateBlue
    BackgroundColor LightYellow
}

skinparam Database{
    BackgroundColor whitesmoke
    BorderColor DarkSlateBlue
}

skinparam Collections{
    BackgroundColor whitesmoke
    BorderColor DarkSlateBlue
}

hide footbox
'[...]
@enduml
Für diese Seite genutzte Formatierung
Für diese Seite genutzte Formatierung

Sequenzdiagramme per Formatierung als Entwurf kennzeichnen

Um den Entwurfscharakter eines Sequenzdiagramms zu unterstützen kann ein Layout verwendet werden, dass Handschrift ähnelt. Besonders wirkungsvoll ist das, wenn auch die Schrift authentisch handschriftlich aussieht. Dies ist z.B. bei der Schriftart “FG Virgil” der Fall. Welche Schriften das jeweilige System bietet lässt sich mit dem PlantUML-Befehl listfonts anzeigen. Wichtig ist: sofern eine Vektorgrafik ausgegeben wird (svg, eps) muss die Schrift auf allen anzeigenden Computern vorhanden sein, andernfalls werden Ersatzschriftarten verwendet.

Beispiel: ( online bearbeitbar / vollständiger Quelltext )

skinparam style strictuml
skinparam DefaultFontName "FG Virgil"
skinparam handwritten true
skinparam monochrome true
skinparam packageStyle rect
skinparam shadowing false
Entwurfscharakter durch Handschriftart hervorherben
Entwurfscharakter durch Handschriftart hervorherben

Diagramme vereinheitlichen und Formatierung auslagern

Damit nicht alle PlantUML-Diagramme die selben skinparam-Sequenzen am Beginn der plantUML-Datei nennen müssen, kann eine zentrale Konfigurationsdatei eingebunden werden, die die Formatierungen enthält:

!includeurl https://PATH.TO/MY/UMLSEQUENCE_CONFIG.cfg

Alle Befehle in dieser Datei werden ausgeführt, als stünden sie in der umgebenden PlantUML-Datei. Um ein einheitliches Layout zu erreichen ist diese Funktion sehr praktisch!

Es finden sich zahlreiche Quellen und Dokumentationen zu PlantUML im Netz, erwähnenswert sind u.a.:

Quellen und offene Ressourcen (OER)

Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:

https://gitlab.com/oer-informatik/uml/umlsequenz.

Sofern nicht explizit anderweitig angegeben sind sie zur Nutzung als Open Education Resource (OER) unter Namensnennung (H. Stein, oer-informatik.de) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).

Creative Commons Lizenzvertrag

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: