PlantUML ER diagram conventions
Table of Contents
ER diagrams document the live SQL schema — every table, its columns, and the foreign-key relationships between them. The compass-doc-add-er-diagram skill drives the authoring; this doc captures how the diagram looks. The schema lives in ores.sql. Return to Knowledge.
1. File location
The schema outgrew one picture, so the diagram is one file per group and an index over them. A group is a package: its tables and the keys it holds inside itself. A key that leaves the group is named in the group's note, with the group it belongs to, because its other end is drawn in another file.
| Item | Value |
|---|---|
| Index | projects/ores.sql/modeling/ores_schema.puml — every group, its file, and its tables |
| Group | projects/ores.sql/modeling/ores_schema.<group>.puml — one per package, committed |
| Drawn | ores_schema.<group>.svg — committed |
| CMakeLists | projects/ores.sql/modeling/CMakeLists.txt |
The .puml files hold what the diagram says and the SVG images are what
a reader opens, so both are committed. The drawing is SVG because the
schema has no raster budget left: the single 463-table diagram overflowed
a 32-bit pixel count before it was split.
Below is the index; every group's image is on the ores.sql component page.
2. Groups
The groups are the model's packages. A package holds its tables and the
keys that stay inside it; a key that leaves is named in the note beside
the group it reaches. The model's packages list is what names them, so
a group cannot drift from the schema.
3. Generation pipeline
The ER diagram is generated, not hand-written. It's the canonical example in the project of the automated codegen route (see MASD for the routes architecture): codegen parses already-generated artefacts, derives a model from them, and renders new artefacts from that model.
The pipeline has three stages, all driven by
projects/ores.codegen/plantuml_er_generate.sh:
| Stage | Reads | Tool | Writes |
|---|---|---|---|
| Parse | projects/ores.sql/create/**/*.sql |
projects/ores.codegen/src/plantuml_er_parse_sql.py |
build/output/codegen/plantuml_er_model.json |
| Render | the intermediate JSON + a mustache template per group, and one for the index | projects/ores.codegen/src/plantuml_er_generate.py (pystache) |
projects/ores.sql/modeling/ores_schema.<group>.puml, and the index at ores_schema.puml |
| Draw | the .puml files |
PlantUML (system plantuml, or plantuml.jar) |
projects/ores.sql/modeling/ores_schema*.svg |
The intermediate model file is not committed and not literate. It
lives under build/output/ (gitignored) because it is regenerated on
every run and carries no information a human is expected to read or
edit. The format is JSON for the same reason: a generated artefact
doesn't benefit from org-mode literate prose, drawers, or block-paste
mechanisms — those are reserved for the authored models humans
maintain (under each component's modeling/ directory).
The .puml files are committed because reviewers and readers benefit
from seeing schema-shape diffs in PRs. The images are committed because
the documentation site renders them: an image link under proj: exports
as an image tag pointing at the file in the repository, so an image that
is not in the tree is a broken page.
4. Skeleton
' GPL header @startuml title ORES Database Schema hide circle skinparam linetype ortho ' <<< BODY GOES HERE >>> ' Local Variables: ' compile-command: "java -Djava.awt.headless=true -DPLANTUML_SECURITY_PROFILE=UNSECURE -DPLANTUML_LIMIT_SIZE=65535 -jar /usr/share/plantuml/plantuml.jar ores_schema.puml" ' End: @enduml
5. Entity styling
Use the entity keyword with the appropriate stereotype:
| Stereotype | Colour | When |
|---|---|---|
<<temporal>> |
#C6F0D8 |
Tables with valid_from / valid_to columns. |
<<junction>> |
#ECECEC |
Many-to-many association tables. |
| (none) | #F7E5FF |
Current-state tables without temporal support. |
Example:
entity accounts <<temporal>> #C6F0D8 {
* id : uuid <<PK>>
--
* username : text <<unique>>
* email : text <<unique>>
* valid_from : timestamptz
* valid_to : timestamptz
}
6. Field notation
*prefix marksNOT NULLfields.<<PK>>for primary keys,<<FK>>for foreign keys,<<unique>>for unique constraints.- A
--separator divides the primary key block from the rest of the columns.
7. Domain packages
Group related entities into packages by domain:
| Domain | Colour |
|---|---|
| IAM (Identity & Access) | #E8F4FD |
| RBAC (Role-Based Access Control) | #FFF3E0 |
| Assets (Images & Currencies) | #F3E5F5 |
| Variability (Feature Flags) | #E8F5E9 |
A package looks like:
package "IAM (Identity & Access Management)" #E8F4FD {
entity accounts <<temporal>> #C6F0D8 {
...
}
}
8. Relationships
Standard ER cardinality:
| Notation | Meaning | Example |
|---|---|---|
\vert\vert--o{ |
One-to-many | accounts \vert\vert--o{ sessions |
\vert\vert--o\vert |
One-to-one | accounts \vert\vert--o\vert login_info |
| Junction table | Many-to-many | Use a <<junction>> entity |
Always label the arrow with descriptive text:
accounts ||--o{ sessions : "creates"
roles ||--o{ role_permissions : "has"
9. Notes
Add a note next to any entity that benefits from explanation:
note right of accounts User accounts with authentication credentials. Supports optimistic locking via the version field. end note
10. Temporal conventions
ORE Studio uses bitemporal data management. Most tables have:
valid_from/valid_to— validity period (temporal support).version— optimistic locking.modified_by— audit trail.
Junction tables typically carry:
assigned_by— who created the association.assigned_at— when the association was created.
11. Schema source
SQL schema lives at projects/ores.sql/create/. Two file kinds:
*_create.sql— table definitions with triggers.*_notify_trigger.sql— PostgreSQL NOTIFY triggers for change events.
Read these to confirm:
- Column types, constraints, primary key shape (incl. composite).
- Indexes (incl. partial indexes for "current" records).
- Temporal trigger presence.
12. Evaluation checklist
| Check | How |
|---|---|
| All tables represented | Compare with projects/ores.sql/create/*_create.sql. |
| FK arrows correct | Verify against the SQL REFERENCES clauses. |
| Proper stereotypes | Temporal tables <<temporal>>, junctions <<junction>>. |
| Domain packages | Related entities grouped logically. |
| Syntax valid | cmake --build --target generate_ores_schema_diagram. |
13. See also
- compass-doc-add-er-diagram skill — drives the authoring.
- PlantUML class diagram conventions — sibling, for components.
- How do I generate PlantUML diagrams? — runs the build.
- ores.sql — the schema this diagram documents.