Skip to content

Mermaid ​

Mermaid renders any Apparatus network as a Mermaid flowchart. Only the structural topology is shown — internal state and transition functions are erased at runtime — but labels make a diagram easy to read.

scala
import apparatus.core.*
import apparatus.core.fix.alg.Mermaid
import apparatus.examples.*
import cats.Id
import cats.implicits.*

Basic usage ​

scala
val diagram: String = Mermaid.print(saga[Id]())

Paste the result into mermaid.live or drop it into a Markdown renderer that supports Mermaid fenced code blocks. Alternatively call .mermaid as an extension method:

scala
val diagram2: String = saga[Id]().mermaid

Attaching labels ​

Call .label("…") on any sub-machine before passing the network to print. The booking saga example bakes labels into every service machine and the orchestrator, so saga() produces a fully annotated network.

Label semantics depend on what the node is:

ExpressionEffect in diagram
closedMealy(m).label("X")Renames the box to X
lmapOrEmpty{…}.label("X")Renames the filter diamond to X
composite.label("X")Wraps the entire sub-graph in subgraph "X"

Shape legend ​

Mermaid shapeApparatus node
["name"] rectangleaggregateMachine, closedMealy, or openMealy — a single machine node
{"name"} diamondAlternative (routing) or LmapOrEmpty (filter)
(["name"]) stadiumStructural split, join, fan-out, combine connectors

Example output ​

Mermaid.print(saga[Id]()) for the three-service booking saga:

Feedback back-edges ​

Apparatus.feedback and Apparatus.feedbackMany produce back-edges labelled A ↺ and N[A] ↺ respectively. Mermaid renders these as arrows that close the loop visually.

Labeling intermediate nodes ​

The most useful pattern is to label every aggregateMachine node that wraps a domain decider and every lmapOrEmpty filter. The SagaStepAdapter.lmapOrEmpty method does this automatically, adding a "<step> event router" label.

Manual labeling:

scala
import apparatus.examples.*
import cats.effect.SyncIO
import java.util.UUID

val labeled: Apparatus[SyncIO, FlightCommand, List[FlightEvent]] =
  Apparatus
    .aggregateMachine[SyncIO, FlightCommand, FlightEvent](flightDecider(), _.id)
    .label("Flight Decider")