Diagrammen maken met Mermaid

Steeds weer kom ik Mermaid tegen als het gaat om het maken van diagrammen. Persoonlijk werk ik graag met (Plant)uml maar meestal moet ik diagrammen dan exporteren naar een afbeelding voordat ik het in de documentatie kan gebruiken.

Microsoft DevOps heeft ondertussen ondersteuning voor Mermaid, Github heeft nu ondersteuning voor Mermaid en aangezien dat twee platformen zijn die ik veelvuldig gebruik lijkt het me tijd om me eens te gaan verdiepen in Mermaid.

Wat is Mermaid

Mermaid is een tool om diagrammen te maken en te visualiseren door middel van het gebruik van code, gebaseerd op Javascript.

Een simpel voorbeeld van een Mermaid diagram in code:

graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;

Ziet er zo uit als afbeelding:

graph TD; A-->B; A-->C; B-->D; C-->D;

Voorbeelden

Op de website van Mermaid staan veel voorbeelden maar niet veel complexe voorbeelden. Ik ga proberen een paar mooie voorbeelden te maken die laten zien wat edMermaid]1 allemaal kan.

Flowchart

Het is mogelijk om verschillende vormen te gebruiken, zo kun je ook designs van je architectuur maken:

graph TD; Customer(("Customer"))--Ordering pizzas-->App; App--Pizza order-->GraphQL_API; GraphQL_API-->Events(["Events"]); Events-->OrderMicroservice["Order Microservice"] OrderMicroservice-->OrderDatabase[("Order Data")] Events-->RestaurantMicroservice["Restaurant Microservice"] RestaurantMicroservice-->RestaurantDatabase[("Restaurant Data")] Events-->DeliveryMicroservice["Delivery Microservice"] DeliveryMicroservice<-->DeliveryDatabase[("Delivery Data")]
graph TD;
    Customer(("Customer"))--Ordering pizzas-->App;
    App--Pizza order-->GraphQL_API;
    GraphQL_API-->Events(["Events"]);
    Events-->OrderMicroservice["Order Microservice"]
    OrderMicroservice-->OrderDatabase[("Order Data")]
    Events-->RestaurantMicroservice["Restaurant Microservice"]
    RestaurantMicroservice-->RestaurantDatabase[("Restaurant Data")]
    Events-->DeliveryMicroservice["Delivery Microservice"]
    DeliveryMicroservice<-->DeliveryDatabase[("Delivery Data")]

Bovenstaande is wel een beetje misbruik maken van een flowchart, onderstaande is iets meer waar het echt voor bedoeld is:

flowchart TD A(Begin)==>B[Plan onderhoudsbeurt in] ==> C{Succesvol?} C-->|nee|D[Internal Server Error] D-->E[Opslaan in wachtrij] E-->F(einde) C===>|Ja|G[verwerk reservering] G==>F(einde) C-->|nee|H[Too Many Requests] --> K{Circuit vaker dan 3x verbroken?} K-->|Nee|I[Verbreek het circuit] K-->|Ja|E[Opslaan in wachtrij] I-->J[Wacht 30 seconden, retry] J-->B

De mermaid code:

flowchart TD
    A(Begin)= =>B[Plan onderhoudsbeurt in] ==> C{Succesvol?}
    C- ->|nee|D[Internal Server Error]
    D-->E[Opslaan in wachtrij]
    E-->F(einde)
    C===>|Ja|G[verwerk reservering]
    G==>F(einde)
    C-->|nee|H[Too Many Requests] --> K{Circuit vaker dan 3x verbroken?}
    K-->|Nee|I[Verbreek het circuit]
    K-->|Ja|E[Opslaan in wachtrij]
    I - -> J[Wacht 30 seconden, retry]
    J-->B

State diagram

Ongeveer hetzelfde diagram als hierboven maar nu met het `state diagram`:

stateDiagram-v2 state request_state <> state error_state <> [*] --> Plan_Onderhoud_In Plan_Onderhoud_In --> request_state request_state --> error_state: Fouten error_state --> Opslaan_In_Wachtrij: InternalServerError Opslaan_In_Wachtrij --> [*] error_state --> ErrorHandling: TooManyRequests %% Verbreek_Het_Circuit --> Plan_Onderhoud_In request_state --> VerwerkReservering: Succes VerwerkReservering --> [*] state ErrorHandling { state retry_state <> [*] --> Retry_Request Retry_Request --> retry_state retry_state --> BreakCircuit: broken < 3 times retry_state --> Opslaan_In_Wachtrij: broken >= 3 times BreakCircuit --> Wait30Seconds Wait30Seconds --> Retry_Request }

stateDiagram-v2
    state request_state <<choice>>
    state error_state <<choice>>
    [*] --> Plan_Onderhoud_In
    Plan_Onderhoud_In --> request_state
    request_state --> error_state: Fouten
    error_state --> Opslaan_In_Wachtrij: InternalServerError
    Opslaan_In_Wachtrij --> [*]
    error_state --> ErrorHandling: TooManyRequests
    request_state --> VerwerkReservering: Succes
    VerwerkReservering --> [*]

    state ErrorHandling {
	state retry_state <<choice>>
	[*] --> Retry_Request
	Retry_Request --> retry_state
	retry_state --> BreakCircuit: broken < 3 times
	retry_state --> Opslaan_In_Wachtrij: broken >= 3 times
	BreakCircuit --> Wait30Seconds
	Wait30Seconds --> Retry_Request
    }

Sequence diagram

Hieronder staat een diagram van een mogelijke applicatie. Er is een menu aan toegevoegd, als je met je muis over de actor `Custom Application` gaat zie je twee menu items verschijnen.

sequenceDiagram autonumber participant Custom Application participant Azure AD B2C participant Azure Api Management participant Azure Blob Storage link Custom Application: Wiki @https://google.com/search?q=mermaid+menu link Custom Application: Source @https://google.com/search?q=mermaid+menu Custom Application->>Azure AD B2C: authenticate Azure AD B2C->>Custom Application: token Azure AD B2C->>Azure Api Management: request new SAS token Azure Api Management->>Azure AD B2C: newly generated SAS token with expiration time Azure Api Management->>Custom Application: newly generated SAS token with expiration time Custom Application->>Azure Blob Storage: upload new file with SAS token
sequenceDiagram
    autonumber
    participant Custom Application
    participant Azure AD B2C
    participant Azure Api Management
    participant Azure Blob Storage
    link Custom Application: Wiki @https://google.com/search?q=mermaid+menu
    link Custom Application: Source @https://google.com/search?q=mermaid+menu
    Custom Application->>Azure AD B2C: authenticate
    Azure AD B2C->>Custom Application: token
    Azure AD B2C->>Azure Api Management: request new SAS token
    Azure Api Management->>Azure AD B2C: newly generated SAS token with expiration time
    Azure Api Management->>Custom Application: newly generated SAS token with expiration time
    Custom Application->>Azure Blob Storage: upload new file with SAS token

C4 Models

Helaas is het nog niet mogelijk om C4 te gebruiken zoals wel mogelijk is met C4-PlantUML. Nu grotere platformen Mermaid omarmen hoop ik dat hier snel ondersteuning voor komt zodat het visualiseren van een architectuur ook met Mermaid en C4 mogelijk wordt.

Het is overigens niet onmogelijk om dergelijke diagrammen te tekenen, er is alleen geen echte C4 ondersteuning voor. Een voorbeeld van het internet:

graph TB Customer--"Uses (https)"-->CustomerApp("Customer Application") subgraph "Customer Information (System)" CustomerApp["Customer Application
(JavaScript/Angular)

Allow customers to manage their profile"]--"Updates customer informationusing
[async, JSON/HTTPS]"-->CustomerService["Customer Service
(Java, Spring Boot)

The point of access for customer information"] CustomerService--"Sends events to
[WebSocket]"-->CustomerApp CustomerService--"Stores data in
[jdbc]"-->CustomerDatabase("Customer Database
(Oracle, 12c)

Stores customer information") CustomerService--"Sends customer update
events to"-->MessageBus MessageBus--"Sends customer update
events to"-->ReportingService MessageBus--"Sends customer update
events to"-->AuditService["Audit Service
[C#,.Net]

Provides organization wide
auditing facilities"] ReportingService["Reporting Service
[Ruby]

Creates normalized data for
reporting purposes"]--"Stores data in"-->ReportingDatabase("Reporting Database
[MySql]

Stores a normalized version
of all business data for
ad hoc reporting purposes") AuditService--"Stores events in"-->AuditStore["Audit Store
[Event store]

Stores information about
events that have happened"] end

test

graph TB
    Customer--"Uses (https)"-->CustomerApp("Customer Application")
    subgraph "Customer Information (System)"
    CustomerApp["Customer Application<br/>(JavaScript/Angular)<br/><br/>Allow customers to manage their profile"]--"Updates customer informationusing<br/>[async, JSON/HTTPS]"-->CustomerService["Customer Service<br/>(Java, Spring Boot)<br/><br/>The point of access for customer information"]
    CustomerService--"Sends events to<br/>[WebSocket]"-->CustomerApp
    CustomerService--"Stores data in<br/>[jdbc]"-->CustomerDatabase("Customer Database<br/>(Oracle, 12c)<br/><br/>Stores customer information")
    CustomerService--"Sends customer update<br/>events to"-->MessageBus
    MessageBus--"Sends customer update<br/>events to"-->ReportingService
    MessageBus--"Sends customer update<br/>events to"-->AuditService["Audit Service<br/>[C#,.Net]<br/><br/>Provides organization wide<br/>auditing facilities"]
    ReportingService["Reporting Service<br/>[Ruby]<br/><br/>Creates normalized data for<br/>reporting purposes"]--"Stores data in"-->ReportingDatabase("Reporting Database<br/>[MySql]<br/><br/>Stores a normalized version<br/>of all business data for<br/>ad hoc reporting purposes")
    AuditService--"Stores events in"-->AuditStore["Audit Store<br/>[Event store]<br/><br/>Stores information about<br/>events that have happened"]
    end

Styling

Mermaid maakt gebruik van verschillende stijlen. In mijn blog gebruik ik de `default` stijl maar er zijn dus andere mogelijkheden. Daarnaast is het ook nog mogelijk om, met behulp van stylesheets, zelf aanpassingen te doen. Een voorbeeld van een aanpassing:

flowchart LR id1(Start)-->id2(Stop) style id1 fill:#f9f,stroke:#333,stroke-width:4px style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
flowchart LR
    id1(Start)-->id2(Stop)
    style id1 fill:#f9f,stroke:#333,stroke-width:4px
    style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5

Configuration

Mermaid is te configureren in javascript:

mermaid.initialize({
    startOnLoad:true,
    theme:"default",
    flowchart: {
	    curve: "linear"
    }
});

Je kunt deze configuratie ook per diagram doen of een globale configuratie overschrijven. In onderstaande voorbeeld gebruik ik geen rechte maar ronde lijnen voor de en het donkere thema flowchart:

%%{init: {"theme": "dark", "flowchart": { "curve": "basis" }}}%% flowchart LR a --> b & c--> d
%%{init: {"theme": "dark", "flowchart": { "curve": "basis" }}}%%
flowchart LR
   a --> b & c--> d

Zelf uitproberen

Ik moet zelf nog erg wennen aan de syntax en de mogelijkheden van Mermaid. Ik ga er de komende tijd mee aan de slag en waarschijnlijk word ik er dan vanzelf wel beter in. Wil je er zelf ook eens mee spelen? Je kunt makkelijk aan de slag met een online editor zodat je niets hoeft te installeren.

Deel je creaties als je die hebt, misschien kan ik deze post dan uitbreiden met wat meer voorbeelden.

Bronnen