PlantUML Guide
PlantUML is the standard diagramming tool for the Arda documentation site. Mermaid should not be used for diagrams embedded in Markdown documents.
Resources
Section titled “Resources”- PlantUML Reference Manual
- Color Names and Samples
- Color Manual
- C4 library: C4-PlantUML on GitHub
- C4 introduction: Software Architecture Modeling with C4
General Rules
Section titled “General Rules”Pair every diagram with a textual summary
Section titled “Pair every diagram with a textual summary”Agents (LLM assistants, MCP clients, search bots) rarely consume images and parse PlantUML source unevenly. Every diagram must be paired with a 1–3 sentence prose summary that captures the same semantic content the diagram conveys.
The summary appears immediately before the diagram, written as ordinary prose (not a heading, not a callout). Treat it as the diagram’s alt text in long form.
The sequence below shows the upload flow from the browser through the BFF to S3. The browser obtains a presigned URL, uploads directly to S3, then notifies the BFF on success.
```plantuml@startumltitle Upload flowactor Browserparticipant BFFparticipant S3Browser -> BFF: GET /upload-urlBFF --> Browser: presigned URLBrowser -> S3: PUT (presigned)S3 --> Browser: 200Browser -> BFF: POST /upload-complete@enduml```The summary should:
- State the diagram’s subject (what is being depicted) and the outcome the reader should take away.
- Use plain US English. Avoid PlantUML jargon (
activation,lifeline) unless the diagram is about PlantUML. - Sit between 80 and 400 characters. Longer narratives belong in the surrounding section, not in the summary.
This rule applies to every diagram type — sequence, class, component, state, deployment. Diagrams used purely as visual decoration (e.g., a logo) are exempt; they should also carry an alt attribute (when embedded as <img>) or a one-line caption.
Embedding in Markdown
Section titled “Embedding in Markdown”Embed a PlantUML diagram using a fenced code block with the plantuml language identifier:
```plantuml@startuml...@enduml```Every diagram must include @startuml / @enduml delimiters — PlantUML will not parse without them.
Colors
Section titled “Colors”Use only named colors (e.g., LightYellow, LightGray, Salmon). Do not use hex codes (#ffffcc) or RGB values. Named colors are more readable and consistent across diagrams.
Validation
Section titled “Validation”All diagrams must be validated before committing. Render to SVG and verify the output visually. Do not commit a diagram that has not been validated.
Titles
Section titled “Titles”Every diagram must have a title declaration immediately after @startuml (and after any configuration directives like hide footbox). The title should describe the scenario or structure being depicted:
@startumlhide footboxtitle Option A: Decoupled Upload + Entity UpdateSequence Diagrams
Section titled “Sequence Diagrams”Footbox
Section titled “Footbox”Always hide the participant footbox. Add hide footbox immediately after @startuml:
@startumlhide footboxtitle My Sequence DiagramParticipants
Section titled “Participants”Declare participants explicitly in left-to-right architectural order (user on the left, deepest backend/infrastructure on the right). Use actor for human participants and participant for system components:
actor Userparticipant SPAparticipant BFFparticipant Operationsparticipant "S3 Bucket" as S3For participants with multi-word names, use quoted strings with an alias: participant "S3 Bucket" as S3.
Activation
Section titled “Activation”Prefer inline activation (++ suffix on the arrow) over explicit activate/deactivate pairs. Inline activation is more concise and keeps the activation scope visually tied to the message:
User -> SPA ++ : Select image fileSPA -> BFF ++ : POST /api/items/upload-urlUse explicit deactivate only when the deactivation point is not immediately after a return message (e.g., after a note block).
Discrete Activation Blocks (Multi-Step Interactions)
Section titled “Discrete Activation Blocks (Multi-Step Interactions)”When a diagram shows multiple user interactions with a front-end participant (e.g., navigate, select, submit), each interaction must produce a separate activation block on the front-end lifeline. Do not let one continuous activation bar span the entire diagram.
Pattern: Each user action activates the front-end with ++. Backend calls are nested inside with their own ++ / return pairs. The outer return deactivates the front-end back to the user. Phase separators (== ... ==) appear between activation blocks, not inside them.
@startumlhide footboxtitle Discrete Activation Blocks Example
actor Userparticipant "Front\nEnd" as FEparticipant "Back\nEnd" as BE
== Navigate ==
User -> FE ++ : Navigate to /items FE -> BE ++ : POST /v1/item/query return 200 OK (items list)return Display Items grid
== Select Item ==
User -> FE ++ : Click item row FE -> BE ++ : GET /v1/item/<itemEId>/full return 200 OK (item details)return Open Item Details panel
note over FE Button enabled only when card status is FULFILLED.end note
== Perform Action ==
User -> FE ++ : Click "Add to cart" FE -> BE ++ : POST /v1/kanban/kanban-card/<eId>/event/request return 200 OK (KanbanCardStateChange) FE -> FE : Show toast:\n"Item added" FE -> BE ++ : GET /v1/kanban/kanban-card/count return 200 OK (count)return Badge count updated@endumlKey rules:
- One activation block per user gesture.
User -> FE ++opens the block;return <display text>closes it. - Nest backend calls inside the front-end block.
FE -> BE ++/returnpairs are indented within the outer activation. - Self-calls stay inside the block.
FE -> FE : ...does not break the activation — it remains part of the enclosing block. - Notes between blocks. Place
note over FE(notnote right of FE) after the closingreturnand before the next phase separator. - Never use
FE --> User --to close a block. Always usereturnso PlantUML manages the deactivation automatically.
Return Messages
Section titled “Return Messages”Use dashed arrows (-->) for return messages and notifications from deeper layers to higher layers:
Operations --> BFF : 200 OK\n(url, formFields, objectKey)Do not use ..> — it is not valid in PlantUML sequence diagrams.
When the return immediately follows the activation, prefer return over explicit deactivate:
BFF -> Operations ++ : POST /items/upload-urlreturn 200 OK\n(url, formFields, objectKey)Phase Separators
Section titled “Phase Separators”Use == ... == separators to divide the diagram into logical phases or steps. Each separator should have a descriptive label:
== Step 1: Request Upload Credentials ==...== Step 2: Upload to S3 ==...== Step 3: Update Entity ==Message Labels
Section titled “Message Labels”Curly braces are not allowed in message labels — PlantUML interprets them as syntax. Use parentheses or angle brackets instead:
| Instead of | Use |
|---|---|
A -> B : 200 OK\n{ url, key } | A -> B : 200 OK\n(url, key) |
A -> B : PUT /items/{itemId} | A -> B : PUT /items/<itemId> |
Curly braces are safe inside note blocks — they are not parsed as PlantUML syntax there.
Use note right of <participant> for implementation details, validation logic, or design notes. Keep notes attached to the participant performing the action:
note right of Operations Generates UUID for asset-key. Constructs S3 key from DQ-001 format.end noteUse note over <participant> for background processes or cross-cutting concerns:
note over Operations Periodic cleanup job runs every 24 hours.end noteMulti-Line Messages
Section titled “Multi-Line Messages”Use \n for line breaks in message labels. Keep to 2-3 lines maximum for readability:
SPA -> BFF ++ : POST /api/storage/upload-url\n(module: operations,\n entityType: item)Class Diagrams
Section titled “Class Diagrams”Show relationships (inheritance, composition, dependency) and key methods/fields. Omit trivial accessors, getters/setters, and boilerplate methods. The diagram should communicate the design, not replicate the source code.
Basic Example
Section titled “Basic Example”@startumltitle Item Domain Model
class AClass { +attr: String +method(p: Int): Double}@endumlRelationships
Section titled “Relationships”Use standard UML notation:
| Relationship | Notation |
|---|---|
| Inheritance | `Child — |
| Implementation | `Class .. |
| Composition | Container *-- Part |
| Aggregation | Container o-- Part |
| Dependency | Client ..> Service |
| Association | A --> B |
Stereotypes
Section titled “Stereotypes”Use stereotypes to indicate the nature of a class when it is not obvious:
class ItemService <<service>>interface S3BucketAccess <<interface>>class ImageKey <<value object>>C4 Model Diagrams
Section titled “C4 Model Diagrams”Use the C4 model for architectural views. The C4 library provides macros for the four levels of detail: Context, Container, Component, and Code.
C4 Container Diagram
Section titled “C4 Container Diagram”@startuml C4_example!include <C4/C4_Container>
title Arda Platform — Container View
Person(user, "Shop Manager", "Uses Arda to manage inventory")Container(frontend, "arda-frontend-app", "Next.js", "Web UI for inventory management")Container(backend, "operations", "Ktor / Kotlin", "Core domain services and REST API")ContainerDb(db, "PostgreSQL", "Database", "Bitemporal inventory data")
Rel(user, frontend, "Uses", "HTTPS")Rel(frontend, backend, "API calls", "REST/JSON")Rel(backend, db, "Reads/Writes", "JDBC")@endumlC4 Shape Gallery
Section titled “C4 Shape Gallery”The following shape macros are available:
@startuml C4_shapes!include <C4/C4_Context>!include <C4/C4_Component>!include <C4/C4_Container>!include <C4/C4_Deployment>
Person(person, "Person")Person_Ext(personExt, "External Person")System(system, "Internal System")System_Ext(systemExt, "External System")SystemDb(db, "Database")SystemQueue(queue, "Message Queue")Container(container, "Container")ContainerDb(containerDb, "Container DB")Component(component, "Component")Deployment_Node(node, "Deployment Node")@endumlC4 Boundary Gallery
Section titled “C4 Boundary Gallery”@startuml C4_boundaries!include <C4/C4_Context>!include <C4/C4_Container>
Boundary(aBoundary, "A Boundary")Enterprise_Boundary(eBoundary, "Enterprise Boundary")System_Boundary(sBoundary, "System Boundary")Container_Boundary(containerBoundary, "Container Boundary")
System_Boundary(b1, "Internal System") { Container(webApp, "Web Application", "Next.js") Container(api, "API Service", "Ktor")}@endumlC4 Layout Directives
Section titled “C4 Layout Directives”LAYOUT_TOP_DOWN() ' Default top-to-bottom flowLAYOUT_LEFT_RIGHT() ' Left-to-right flowHIDE_STEREOTYPE() ' Remove stereotype labels from shapesCommon Pitfalls
Section titled “Common Pitfalls”| Pitfall | Fix |
|---|---|
..> in sequence diagrams | Use --> for dashed return arrows |
{braces} in message labels | Use (parentheses) or <angle brackets> |
Missing hide footbox | Always add after @startuml |
| Unvalidated diagrams | Always render and verify before committing |
| Overly detailed class diagrams | Show design-relevant members only |
Missing @startuml/@enduml | Required — PlantUML will not parse without them |
| Mermaid syntax in Markdown | Use PlantUML exclusively per workspace conventions |
| Continuous activation bar spanning entire diagram | Use discrete activation blocks — one User -> FE ++ / return pair per user gesture |
Hex color codes (#ffffcc, #333333) | Use named colors (LightYellow, DarkSlateGray) — see Color Manual |
Conventions for Arda Diagrams
Section titled “Conventions for Arda Diagrams”- Use C4 Container or Component diagrams for architecture overviews.
- Use sequence diagrams for API flows and service interactions.
- Use class diagrams for domain model documentation.
- Use
HIDE_STEREOTYPE()in C4 diagrams for cleaner output. - Keep diagram titles concise and include the view level (e.g., “Container View”, “Sequence: Create Item”).
- Store complex, reused diagrams as separate
.pumlfiles in_assets/plantuml/and include them via!include.
Copyright: © Arda Systems 2025-2026, All rights reserved