Mermaid UML diagrams: class, sequence, and state modeling
UML has long been the language of software design—class diagrams for domain models, sequence diagrams for interactions, state diagrams for workflows. But UML tools were expensive and cumbersome. Mermaid brings UML into the modern age: draw diagrams in text, version them in git, and embed them in any doc.
This guide unifies three core UML diagram types in Mermaid and shows how to use them together for end-to-end design documentation.
Why UML matters (and why Mermaid makes it practical)
UML standardizes how teams think about design:
- Class diagrams show what entities exist and how they relate
- Sequence diagrams show when and how entities interact
- State diagrams show how entities change over time
Traditional UML tools require special software, export to proprietary formats, and rot in a shared drive. Mermaid UML is text, so it:
- Lives next to code in git
- Diffs clearly in pull requests
- Embeds anywhere (README, docs, design docs)
- Survives tool changes (Lucidchart closes? Your diagrams are still text)
Class diagrams: domain modeling
A class diagram models your domain—entities, their attributes, methods, and relationships. It's the blueprint for your data model and API schema.
Here's a simple e-commerce domain:
classDiagram
class User {
int id
string email
string password_hash
datetime created_at
register(email, password)
login(email, password)
}
class Order {
int id
int user_id
decimal total
string status
datetime created_at
add_item(product, quantity)
calculate_total()
ship()
}
class Product {
int id
string name
decimal price
int stock
update_stock(quantity)
}
class OrderItem {
int order_id
int product_id
int quantity
decimal unit_price
}
User "1" -- "*" Order: places
Order "1" -- "*" OrderItem: contains
OrderItem "*" -- "1" Product: includes
This diagram says:
- A User can place many Orders (1:many)
- An Order contains many OrderItems (1:many)
- An OrderItem references one Product (many:1)
- Each class has attributes and methods
Reading the diagram:
- Box name = class name
- Top section = attributes (with types)
- Bottom section = methods (public functions)
- Lines = relationships (cardinality:
1= one,*= many)
Relationships in UML
| Relationship | Syntax | Meaning |
|---|---|---|
| Association | -- | one class references another |
| Inheritance | <|-- | child extends parent |
| Composition | *-- | parent owns child (child can't exist alone) |
| Aggregation | o-- | parent contains child (child can exist alone) |
| Dependency | ..> | class uses another temporarily |
Example:
classDiagram
class Animal {
string name
eat()
sleep()
}
class Dog {
bark()
}
class Cat {
meow()
}
class Person {
Pet pet
feed(pet)
}
Dog --|> Animal: inherits
Cat --|> Animal: inherits
Person "*" -- "1" Animal: owns
A Dog and Cat both inherit from Animal (they are-a Animal). A Person owns an Animal (they have-a Animal). Use this diagram in your design docs to clarify inheritance and ownership.
Antipattern: too many classes in one diagram
A class diagram with 30+ classes is hard to read. Break it into layers:
classDiagram
class UserDTO {
int id
string email
}
class User {
int id
string email
Password password
register()
}
class Password {
string hash
verify(plaintext)
}
UserDTO ..> User: maps to
User "1" -- "1" Password: contains
Domain layer (core entities) separate from API layer (DTOs). Clarifies what crosses the API boundary.
Sequence diagrams: interaction modeling
A sequence diagram shows when entities communicate. It's the "happy path" through your system—actors and their messages over time.
Here's a user login flow:
sequenceDiagram
participant U as User
participant App
participant Auth as Auth Service
participant DB as Database
U->>App: click "Log in"
App->>App: open login form
U->>App: enter email + password
App->>Auth: POST /login (email, password)
Auth->>Auth: hash password
Auth->>DB: SELECT * FROM users WHERE email = ?
DB-->>Auth: user record (with password_hash)
Auth->>Auth: compare hashes
alt Hashes match
Auth->>Auth: generate JWT
Auth-->>App: 200 OK (token)
App->>U: redirect to /dashboard
else Hashes don't match
Auth-->>App: 401 Unauthorized
App->>U: show "Invalid credentials"
end
Reading the sequence diagram:
- Top boxes = actors (User, App, Auth Service, Database)
- Arrows
->>= request (solid line) - Dashed arrows
-->= response - Boxes around steps = logic or loops (alt, loop, par, etc.)
- Time flows top to bottom — first message at top, last at bottom
Sequence diagram patterns
Happy path only:
sequenceDiagram
Client->>API: POST /orders
API->>DB: insert order
DB-->>API: order_id
API-->>Client: 200 OK
With error handling:
sequenceDiagram
Client->>API: POST /orders
API->>Validator: validate request
alt Valid
Validator-->>API: OK
API->>DB: insert
DB-->>API: order_id
API-->>Client: 200
else Invalid
Validator-->>API: error
API-->>Client: 400 Bad Request
end
With retries:
sequenceDiagram
Client->>API: request
loop Retry up to 3 times
API->>Service: call service
alt Service responds
Service-->>API: result
break Exit on success
else Service timeout
API->>API: wait, then retry
end
end
API-->>Client: result
Use alt, loop, break, par to model control flow. Keeps the happy path readable while handling edge cases.
Antipattern: too many actors or messages
A sequence diagram with 15+ participants or 50+ messages is a wall of text. Break it into smaller flows:
- User login → separate diagram
- Order creation → separate diagram
- Payment processing → separate diagram
Link them: "After login (see [User login sequence]), the user creates an order (see [Order creation sequence])."
State diagrams: workflow modeling
A state diagram models how an entity changes over time. It's essential for entities with clear states: orders, users, payments, subscriptions.
Here's an order lifecycle:
stateDiagram-v2
[*] --> Pending
Pending --> Confirmed: payment received
Confirmed --> Shipped: ready to go
Shipped --> Delivered: arrives at address
Delivered --> [*]
Pending --> Cancelled: user cancels
Confirmed --> Cancelled: user cancels
Cancelled --> [*]
Shipped --> Returned: customer returns
Returned --> Refunded: process refund
Refunded --> [*]
Reading the state diagram:
- Boxes = states (Pending, Confirmed, Shipped)
- Arrows = transitions (labeled with the event that triggers them)
[*]= start and end
This diagram tells reviewers: "An order starts as Pending, moves to Confirmed on payment, then Shipped, then Delivered. At any point before Shipped, it can be Cancelled. After Shipped, it can be Returned for a refund."
Modeling guards and actions
Some transitions are conditional. Use guards (in brackets):
stateDiagram-v2
[*] --> Pending
Pending --> Confirmed: payment_received
Confirmed --> Processing: [stock available]
Confirmed --> Backorder: [stock unavailable]
Processing --> Shipped: ready
Backorder --> Processing: stock restocked
Shipped --> Delivered: arrives
Delivered --> [*]
Guard [stock available] means the transition only happens if that condition is true.
Use actions to show what happens during a transition:
stateDiagram-v2
[*] --> Init
Init --> Active: activate() / send_welcome_email()
Active --> Suspended: suspend() / log_event()
Suspended --> Active: reactivate()
Active --> Inactive: deactivate() / archive()
Inactive --> [*]
activate() / send_welcome_email() means: when activate() is called, send a welcome email. Keeps the code-to-diagram mapping clear.
Bringing it together: UML design flow
For a complete design, model all three UML types together:
- Class diagram — entities and domain model
- Sequence diagram — happy path through the system
- State diagram — entity lifecycle
Example: designing a subscription system.
Class diagram:
classDiagram
class Subscription {
int id
string plan
datetime start
datetime end
string status
activate()
pause()
cancel()
renew()
}
class Plan {
string name
decimal price
list features
}
class User {
int id
Subscription subscription
}
User "1" -- "0..1" Subscription: has
Subscription "*" -- "1" Plan: uses
Sequence diagram (activate):
sequenceDiagram
User->>App: click "Activate"
App->>Stripe: create subscription
Stripe-->>App: subscription_id
App->>DB: INSERT subscription
DB-->>App: success
App->>Email: send activation email
App-->>User: redirect to dashboard
State diagram (lifecycle):
stateDiagram-v2
[*] --> Pending
Pending --> Active: activate()
Active --> Paused: pause()
Paused --> Active: resume()
Active --> Cancelled: cancel()
Cancelled --> [*]
Together, these three diagrams paint a complete picture:
- What entities exist (classes)
- How they interact (sequence)
- How they change (state)
FAQ
Should I write the class diagram first or the sequence diagram?
Start with the domain (class diagram) to model entities. Then sequence diagrams to show how they interact. Then state diagrams for entity lifecycles. They feed each other—often you'll iterate.
How detailed should a class diagram be?
Include attributes and relationships that your team needs to understand the design. Don't include every private method or every attribute. Use the diagram as a north star, not a replica of the code.
Can I show multiple sequences for different paths (happy path, error path, etc.)?
Yes, but keep them in separate diagrams and link them. One sequence per main flow keeps it readable.
Should I update diagrams when the code changes?
Yes. Treat them as living docs. If an entity gains a new relationship or a flow changes, update the diagram in the same PR. This is why versioning them in git matters.
How do I enforce these UML conventions on my team?
Document your conventions in a DESIGN.md file (class diagram structure, naming, what to include). Share templates (see Mermaid reusable diagram components) so everyone starts from the same base.
Master UML modeling with Mermaid. Draft your domain model in the MermaidCreator editor and let your next design review start with a clear diagram.
Related posts
Mermaid dependency graphs for software architecture
Map software dependencies and component relationships with Mermaid. Learn syntax, real-world examples, and best practices for dependency visualization.
Mermaid block diagrams for system architecture
Sketch system architecture using Mermaid block diagrams—simple, text-based boxes and connections for design reviews, documentation, and onboarding.