Learn (Almost) Anything
CatalogDownload course

Diagrams as Code with Mermaid

Two related modeling tools in one sitting. classDiagram for object structure — attributes, methods, visibility, and relationships (inheritance, composition, association). erDiagram for database schemas — entities, attributes, primary/foreign keys, and crow's-foot cardinality (one-to-many, many-to-many). When to reach for which, and how to keep both honest as the model changes.

enMini-course6 lessons1 module5
Diagrams as Code with MermaidMermaid Diagrams in Practicemermaiddiagramscodepractice

Class and ER Diagrams for Schemas

Two related modeling tools in one sitting. classDiagram for object structure — attributes, methods, visibility, and relationships (inheritance, composition, association). erDiagram for database schemas — entities, attributes, primary/foreign keys, and crow's-foot cardinality (one-to-many, many-to-many). When to reach for which, and how to keep both honest as the model changes.

Class and ER Diagrams for Schemas

Quick orientation: classDiagram models object structure — class attributes, methods, visibility, and type relationships in code. erDiagram models database schemas — tables, columns, primary/foreign keys, and crow's-foot cardinality. The same system often needs both: your User class might expose a logout() method the database has no concept of, while the users table carries created_at columns the domain model treats as plumbing. Don't force one diagram to do both jobs.

classDiagram

Keyword is classDiagram — no direction argument, layout is automatic. Define members inside curly braces or with colon notation:

classDiagram
    class User {
        +int id
        +String email
        -String passwordHash
        +login() bool
        +logout()
    }

The parser spots methods by () — no parentheses means attribute. Return types trail the closing paren: login() bool.

Visibility modifiers:

SymbolMeaning
+public
-private
#protected
~package/internal

Append $ for static and * for abstract: getInstance()$, validate()*.

Relationship types:

ArrowTypeReads as
`A <\-- B`Inheritance
A *-- BCompositionA owns B; B can't exist alone
A o-- BAggregationA has B; B lives independently
A --> BAssociationA references B
`A <\.. B`Realization
A <.. BDependencyB depends on A

Arrowhead points toward the parent: Animal <|-- Dog means Dog inherits from Animal.

Visual reference chart showing all six UML class diagram relationship arrow types with their notation symbols.

Add multiplicity labels in quotes and a verb label after a colon:

Order "1" --> "*" LineItem : contains

Mark a class as an interface with a stereotype inside the class block:

class Publishable {
    <<interface>>
    publish()
    unpublish()
}
classDiagram class Publishable { <<interface>> publish() unpublish() } class User { +int id +String email -String passwordHash +login() bool +logout() } class Post { +int id +String title +String body +bool published +publish() +unpublish() } class Comment { +int id +String body +timestamp createdAt } Publishable <|.. Post : realizes User "1" --> "*" Post : authors Post "1" *-- "*" Comment : owns
classDiagram
    class Publishable {
        <<interface>>
        publish()
        unpublish()
    }
    class User {
        +int id
        +String email
        -String passwordHash
        +login() bool
        +logout()
    }
    class Post {
        +int id
        +String title
        +String body
        +bool published
        +publish()
        +unpublish()
    }
    class Comment {
        +int id
        +String body
        +timestamp createdAt
    }
    Publishable <|.. Post : realizes
    User "1" --> "*" Post : authors
    Post "1" *-- "*" Comment : owns
classDiagram: a blog domain model showing visibility, stereotypes, and three relationship types

---

erDiagram

Switch keyword to erDiagram. Entity names are conventionally uppercase. Attribute blocks use type name [constraint] syntax:

erDiagram
    USER {
        int id PK
        string email UK
        string password_hash
    }
    ORDER {
        int id PK
        int user_id FK
        timestamp created_at
    }
    USER ||--o{ ORDER : "places"

Supported constraint tags: PK, FK, UK. Combine when needed (int id PK, FK) and add a trailing comment in double quotes: int id PK "auto-increment".

Cardinality is where most people stumble. Each relationship token has three parts:

ENTITY_A [left-marker][line][right-marker] ENTITY_B : "label"

Line type: -- for identifying (solid), .. for non-identifying (dashed).

Cardinality markers:

SymbolMeaning
`\\
`\o / o\
`}\ / \
}o / o{Zero or more
Crow's-foot notation symbol reference for ER diagrams, mapping each line-end marker to its cardinality meaning.

Read each side independently. USER ||--o{ ORDER : "places"|| on the User side (exactly one), o{ on the Order side (zero or more). One User places zero or more Orders.

The label after : is required — omitting it causes a parse error. Quote any label with spaces.

erDiagram USER { int id PK string email UK string password_hash } POST { int id PK int user_id FK string title text body boolean published } COMMENT { int id PK int post_id FK int user_id FK text body } USER ||--o{ POST : "authors" POST ||--o{ COMMENT : "has" USER ||--o{ COMMENT : "writes"
erDiagram
    USER {
        int id PK
        string email UK
        string password_hash
    }
    POST {
        int id PK
        int user_id FK
        string title
        text body
        boolean published
    }
    COMMENT {
        int id PK
        int post_id FK
        int user_id FK
        text body
    }
    USER ||--o{ POST : "authors"
    POST ||--o{ COMMENT : "has"
    USER ||--o{ COMMENT : "writes"
erDiagram: the matching database schema with PK/FK annotations and crow's-foot cardinality

Test your cardinality recall before moving on:

InteractiveER Cardinality Symbol Quiz
Four questions on Mermaid erDiagram cardinality markers. Click an answer to get instant feedback, then advance to the next question.

---

Which one for which job?

  • classDiagram: documenting code — domain models, service interfaces, library APIs.
  • erDiagram: documenting storage — database schemas, migrations, data dictionaries.

They can and should coexist in the same docs. Different audiences, different jobs — don't force one to serve both.

---

Keeping both honest as the model changes

Diagrams drift. A few habits help:

1. Co-locate with the code they describe. An erDiagram lives in the same directory as your migrations. When a migration changes, the diagram is right there.

db/
├── migrations/
│   ├── 001_create_users.sql
│   └── 002_create_posts.sql
└── schema.md   ← erDiagram lives here

2. Keep entities flat. Show only columns worth explaining to a newcomer — audit timestamps and soft-delete flags can usually stay out.

3. Prefer two focused diagrams over one comprehensive one. When an entity grows past ~8 attributes, split into a relationship view and a full-attribute reference.

4. Use subgraphs in classDiagram to mark service boundaries — same technique as the flowchart submodule. Wrap related classes in a subgraph to show module ownership without drawing a line for every dependency.

5. Mermaid won't catch logical errors. You can write contradictory cardinality and it'll render both arrows without complaint. Cross-check tokens against your actual FK constraints before publishing.

---

Quick check

1. Write a classDiagram with Post, Author (one-to-many association), Comment (composition), and a Publishable interface that Post realizes.

2. Write the matching erDiagram with posts, users, and comments tables — mark PKs and FKs.

3. Express "a post has zero or more comments; a comment belongs to exactly one post" as a relationship token — write it from memory, then verify against the table.

State diagrams and Gantt charts are next — a shift from modeling structure to tracking flow over states and work over time.

Self-check

  1. You're documenting a system where `User` objects have a `logout()` method but the `users` database table has no `logout` column. Which statement best explains why you need both a `classDiagram` and an `erDiagram` rather than forcing one to cover both?
  2. In a `classDiagram`, you mark `password_hash` as `-String password_hash`. What does the leading `-` tell a reader about how this field should be accessed?
  3. The relationship `CUSTOMER ||--o{ ORDER : "places"` describes the cardinality between customers and orders. Which statement correctly interprets both markers?
  4. You're modeling a `Document` that contains `Page` objects. If deleting a document should automatically delete its pages, but individual pages can't be created without a document, which relationship type correctly captures this?
  5. In a `classDiagram`, how does Mermaid distinguish between an attribute and a method?
  6. You write `Animal <|-- Dog` to show that Dog inherits from Animal. Why does the arrow point toward `Animal` rather than toward `Dog`?
  7. Your team has been using an `erDiagram` to document the database schema, but a recent migration added columns that haven't been reflected in the diagram. Which practice from the article would most directly prevent this kind of drift?
  8. When a `classDiagram` member like `+login() bool` doesn't appear in the corresponding `erDiagram`, what does this most likely mean?

Homework

Class Diagram for an E-Commerce Product Catalogimage

You're documenting code for an e-commerce product catalog. Create a classDiagram modeling Product, Category, Review, and User classes. Show their attributes with types, at least 3 methods (with mixed visibility), and at least 2 relationship types. Include an interface somewhere if it makes sense.

- Renders without errors (syntactically valid Mermaid) - 4+ classes defined with attributes and types - 3+ methods with at least one return type, showing mixed visibility (+, -, #) - 2+ different relationship types using correct syntax (inheritance, composition, aggregation, association) - If an interface is used, properly marked with <<interface>>

ER Diagram for the Same Catalog (Database Schema)image

Now model the same catalog as a database schema. Create an erDiagram with at least 4 entities (tables) — corresponding to your classes but thinking in database terms. Mark primary keys (PK) and foreign keys (FK). Use crow's-foot cardinality notation for relationships and label every relationship. Don't force everything into the database model; some class attributes (methods, interfaces) stay in code only.

- Renders without errors (syntactically valid Mermaid) - 4+ entities with meaningful names corresponding to your classes - At least 2 primary keys and 2 foreign keys marked - 3+ relationships with correct crow's-foot cardinality markers (||, |o, }|, }o) - Every relationship has a label (no unlabeled relationships) - Logical consistency: FKs reference PKs, cardinality reflects the business logic from your class model

Reconciliation: Comparing Models and Explaining Divergencetext

Compare your two diagrams. Write 200–300 words analyzing: - At least 3 things in your class diagram that don't appear in the ER diagram, and why - At least 2 things in your ER diagram that don't appear in the class diagram, and why - One relationship shown in both notations, with its syntax in each - One real scenario where you'd update one diagram but not the other Be specific to your own diagrams, not generic.

- 200–300 words - Identifies 3+ class-diagram-only elements (e.g., methods, visibility modifiers, interfaces) with reasoning - Identifies 2+ ER-diagram-only elements (e.g., surrogate keys, audit columns, normalization) with reasoning - Compares one relationship in both notations, showing the actual syntax from each diagram - Provides a realistic scenario where one diagram changes without changing the other - References specific class/entity names and relationships from the learner's own work

Submit and get assignments reviewed in the Learn (Almost) Anything app.

Sources

  1. Mermaid — Class Diagram Syntax
  2. Mermaid — Entity Relationship Diagram Syntax