Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sequence diagrams

A sequence diagram shows the messages exchanged between participants over time. Start a file with diagram sequence. The optional title follows it.

diagram sequence "Ordering a pizza"

actor "Customer" as c
participant "Web app" as web
participant "Kitchen" as k

c -> web : place(order) {
  web ->> k : cook(order)
  return confirmation
}
Ordering a pizza Customer Web app Kitchen place(order) cook(order) confirmation

Participants

participant declares a lifeline drawn as a box, and actor declares one drawn as a stick figure. A participant has a name, optionally an alias with as, and optionally a <<stereotype>>. Participants become columns in declaration order, so the file controls the left-to-right reading order, and a name must be declared before it is used.

diagram sequence "Heads"

actor "Operator" as op
participant "Controller" as ctl <<service>>
participant Disk

op -> ctl : mount()
ctl ->> Disk : spin up
Heads Operator «service» Controller Disk mount() spin up

Messages

Arrows select the kind of message, following UML 2.5.1 clause 17.4.4.

ArrowMessageLine and head
a -> bsynchronous callsolid, filled head
a ->> basynchronous messagesolid, open head
a --> breplydashed, open head

A message may carry a label after :. Labels wrap when they are long, and the column spacing grows to fit the widest label between two lifelines. A label that holds a literal { should be quoted: : "set { a }".

A message from a lifeline to itself is drawn as a compact loop with its label on the right. A synchronous self-call also opens a short nested execution bar.

diagram sequence "Kinds of message"

participant Client
participant Server

Client -> Server : request()
Server --> Client : response
Client ->> Server : fire and forget
Server -> Server : log the request
Server ->> Server : schedule cleanup
Kinds of message Client Server request() response fire and forget log the request schedule cleanup

Execution blocks

Follow a message with { to open an execution on the receiver. The block closes with }, and execution bars nest and step to the right as calls go deeper. return sends a reply from inside a block, to the block’s caller. If return is the last statement of the block, the bar ends there; otherwise it ends where the block ends. Inside a block, return takes free text, so it needs no quoting. The block may be written on one line.

diagram sequence "Nested calls"

participant "main()" as m
participant Parser
participant Lexer

m -> Parser : parse(source) {
  Parser -> Lexer : next() {
    Lexer -> Lexer : skip whitespace
    return token
  }
  Parser -> Parser : build tree
  return ast
}
Nested calls main() Parser Lexer parse(source) next() skip whitespace token build tree ast

Creating and destroying

A dashed arrow to a head drawn at the moment of creation shows object creation: write sender -> new "Name" as alias : label, with optional <<stereotype>>. Inside a block the short form create Name : label is also accepted, and it uses the block’s receiver as the sender. destroy name ends a lifeline with an X. A participant that has been destroyed cannot be used afterwards; a destroy in only one alt branch stays open for the other branches.

diagram sequence "Worker lifetime"

participant Pool
Pool -> new "Worker" as w : spawn()
Pool -> w : run(job) {
  return result
}
Pool ->> w : stop
destroy w
Worker lifetime Pool Worker spawn() run(job) result stop

Found and lost messages

[-> a is a found message: its sender is outside the diagram, drawn as a filled circle to the left. a ->] is a lost message, leaving towards a filled circle on the right. Inside a block opened by a found message, return goes back to the outside.

diagram sequence "Boundaries"

participant Bus
participant Handler

[-> Bus : interrupt
Bus -> Handler : dispatch() {
  Handler ->] : retransmit (no ack)
  return handled
}
Boundaries Bus Handler interrupt dispatch() retransmit (no ack) handled

Combined fragments

alt, opt, loop, break, critical and neg wrap messages in a frame with the operator in a pentagon at the top left and a [guard] above the first event. else adds operands to alt, and adds them to par, and each operand is split by a dashed line. Frames size themselves around their contents and nest. A loop guard written in parentheses, such as (1,5), joins the operator as loop(1,5), following the textual syntax of clause 17.6.4.

diagram sequence "Retries and alternatives"

participant Client
participant Server
participant Cache

loop "(1,3)" {
  Client -> Server : fetch(key) {
    alt "cached" {
      Server -> Cache : get(key)
      Cache --> Server : value
    } else "miss" {
      Server -> Server : recompute
      opt "worth caching" {
        Server ->> Cache : put(key, value)
      }
    }
    return value
  }
}
Retries and alternatives opt [worth caching] alt [cached] [miss] loop(1,3) Client Server Cache fetch(key) get(key) value recompute put(key, value) value
diagram sequence "Parallel and critical regions"

participant "Task A" as a
participant Lock
participant "Task B" as b

par {
  a -> Lock : acquire()
} and {
  b -> Lock : acquire()
}
critical "mutex held" {
  Lock --> a : granted
  a -> a : update state
  a -> Lock : release()
}
Parallel and critical regions par critical [mutex held] Task A Lock Task B acquire() acquire() granted update state release()

Interaction references and notes

ref over a, b : Name draws an interaction use: a frame with a ref tab that stands for another interaction.

note over a: text, note left of a: text and note right of a: text add notes with a folded corner, on their own row. note over a, b: text spans several lifelines.

diagram sequence "Boot"

participant Bootloader as bl
participant Kernel as k
participant Init as i

ref over bl, k : Verify signature
bl -> k : jump(entry)
note over k: Interrupts stay masked until the scheduler starts.
k -> new "PID 1" as p : exec
note right of p: Everything runs under this process.
Boot Bootloader Kernel Init PID 1 ref Verify signature Interrupts stay masked until the scheduler starts. Everything runs under this process. jump(entry) exec

Layout

Columns are spaced from the content between them: labels, self-message loops, notes and frame contents. Rows follow an 8px grid, and each row grows to fit its label. Message labels sit above their line, in the gap beside the sender when a message crosses other lifelines, so that text never runs over a lifeline or an execution bar.

Heads stay at the top and are not repeated at the bottom. A diagram has no sd frame: UML Annex A lets the frame and its heading be omitted when the page bounds the drawing, and a diagram sequence "Title" title is a caption outside the diagram. A found message starts, and a lost message ends, on a filled circle (17.4.4), and both keep the arrowhead of their kind: filled for ->, open for ->>.

The lexer detail worth knowing: a label runs to the end of the line, except for a comment (# or //) and a space-separated { or }. Quote the label to keep those as text.