Database Schema Diagrams in Confluence with Mermaid ER (Copy-Paste)
The bug report says "orders show twice in the report" and the fix takes three days because nobody on the team is sure whether order_lines links to orders on order_id or source_order_id, and both columns exist. The schema is documented — in a schema.md that lists columns as bullets and a diagram that was last exported when the DB was two migrations younger. The join question is exactly the thing an ER diagram answers at a glance, and the thing a bulleted column list cannot answer at all.
Quick answer: document the database schema in Confluence as a Mermaid erDiagram, rendered by Mermaid Plus for Confluence. Entities hold keys and attributes; relationship lines carry the cardinality and the join's noun. When the discussion turns to the domain model the code exposes, add a classDiagram on the same page. This post gives you three copy-paste examples.
Why a schema needs a diagram, not a column list
A column list tells you what exists. An ER diagram tells you how it fits — and the fit is where the bugs live:
- Which column is the join? Two tables can share many columns; the diagram names the relationship ("places order"), and the cardinality markers say how many.
- Which side is 'many'? Crow's-foot notation (
|,o,{) encodes one-to-many directly — no counting rows to find out. - What breaks first? When a migration drops or renames a column, the review reads the ER diff and sees the impact before it ships.
Because the diagram is text, it diffs and it reviews. The migration PR contains one line changed in the ER block, and the wiki stays honest with zero extra effort.
Copy-paste 1: the core order tables
The shape every commerce team knows — customer, orders, order lines. Note how the relationship line names the join and the keys sit in the entities:
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_LINE : contains
ORDER ||--o{ PAYMENT : collects
CUSTOMER {
int id PK
string email
string name
}
ORDER {
int id PK
int customer_id FK
datetime placed_at
string status
}
ORDER_LINE {
int id PK
int order_id FK
string sku
int quantity
int price_cents
}
PAYMENT {
int id PK
int order_id FK
string method
int amount_cents
}
The keys sitting inside the entities are the point: ORDER.customer_id is visible in the same box as the relationship that uses it, so the reader verifies the join against the column without scrolling to a separate key listing.
Copy-paste 2: attribute mapping with keys on the table
When the schema belongs to a broader system — users, teams, roles — group the entities and keep the relationship labels meaningful. The classic many-to-many through a join table:
erDiagram
USER }o--o{ TEAM : "believes in"
USER }o--o{ ROLE : "is granted"
TEAM ||--o{ MEMBERSHIP : "enrolls"
USER ||--o{ MEMBERSHIP : "owns"
ROLE ||--o{ MEMBERSHIP : "scopes"
USER }o--o{ TEAM means many users belong to many teams — a relationship that only resolves through MEMBERSHIP. When a reviewer asks "how are users scoped?", the answer is the three lines of this diagram: users hold roles, and both flow through memberships.
Copy-paste 3: when the discussion is the domain model
Same store, different question. The debate is "should checkout live on the order or the payment?" — that is a domain model discussion, and a classDiagram shows the objects the code exposes rather than the tables that store them:
classDiagram
class Customer {
+String email
+placeOrder() Order
}
class Order {
+String status
+addLine(sku, qty)
+pay(method) Payment
}
class OrderLine {
+String sku
+int quantity
}
class Payment {
+String method
+int amountCents
}
Customer "1" --> "0..*" Order : places
Order "1" --> "1..*" OrderLine : contains
Order "1" --> "0..*" Payment : collects
Multiplicity on the arrows ("1" --> "0..*") is the class-diagram answer to ER cardinality — the object version of the same truth. Keep both: erDiagram for the schema page, classDiagram for the design-discussion page, and cross-link them.
Keeping the schema diagram honest
Two habits keep an ER diagram from joining the pile of "this is outdated" docs:
- Ship it with the migration. The ALTER TABLE and the one-line ER diff land in the same review. A diagram updated in the change that moves the schema cannot drift.
- Detail follows questions. If joins only matter in the reporting team, keep the PAYMENT table's columns but skip the audit-log entity nobody queries. A diagram nobody reads is a diagram nobody corrects.
The schema's other half is the API that exposes it; keep the request/response flow beside the tables with a sequence diagram, and the service that owns the tables documented with its class structure.
FAQ
Does Confluence have a built-in database ER diagram tool?
No — Confluence has no native diagramming. Mermaid Plus for Confluence renders erDiagram from editable text with a cached live preview.
How do I show cardinality in a Mermaid ER diagram?
The line markers encode the count: || is exactly-one, o{ is zero-to-many, |{ is one-to-many. Read the marker at each end — the crow's-foot end is the 'many' side.
ER diagram or class diagram for a schema?
erDiagram for table/join truth; classDiagram for the domain model the code exposes. Most teams keep one of each, linked from the schema page.
How do I keep the schema diagram from drifting? Update it in the same review that writes the migration — editable text in Confluence makes the diagram travel with the change.
Render every Mermaid diagram type — ER, class, sequence, state, flowchart and 24 more — in Mermaid Plus for Confluence, free for up to 10 users.
Try Mermaid Plus for Confluence
All 29 Mermaid diagram types with live preview and one-click templates — free for up to 10 users.
Get it on the Atlassian Marketplace