Entity map
The document is the central entity. Versions, comments, permissions, tags and audit events connect around it.
A schema designed for systems that remember.
A multi-tenant PostgreSQL foundation for documents, immutable version history, workspace-scoped tags, threaded comments, document permissions and durable audit events.
01 / Architecture
Every table has a focused responsibility. Foreign keys preserve relationships while workspace identifiers keep tenant data partitioned.
The document is the central entity. Versions, comments, permissions, tags and audit events connect around it.
Login identities and display names.
Workspace-scoped document records and workflow status.
Immutable snapshots created on every save.
Flat labels and their many-to-many association.
Threaded discussions with self-referencing replies.
One viewer, editor, or admin role per user/document pair.
Append-only record of meaningful mutations.
02 / Design principles
The schema places important integrity guarantees in PostgreSQL instead of relying only on application code.
Each save inserts a new version. Historical rows are never updated or deleted during normal operation.
document_versionsworkspace_id partitions documents and tags by tenant without pretending the external workspace service is local.
workspace_idDocument-level access is explicit: viewer, editor, or admin, with one role per user/document pair.
permissionsAudit events preserve meaningful actions and JSONB payloads, even when related records are removed.
audit_log03 / Integrity layer
These are the rules that stop invalid states before they become production data problems.
UUIDs identify core records. The document_tags join table uses a composite key because the relationship itself has no independent identity.
Prevent duplicate emails, workspace slugs, version numbers, tag names and permission grants.
Delete behavior is intentional: cascade dependent records, restrict destructive ownership changes and preserve audit history with SET NULL.
Status, role, action and version values are limited to valid domain values.
04 / Query layer
The data model is designed around common reads: opening a document, listing workspace documents and searching document content.
The most frequent read joins the document to its latest immutable version and owner.
SELECT
d.id,
d.slug,
d.title,
d.status,
dv.version,
dv.body,
u.display_name AS owner_name
FROM documents d
JOIN document_versions dv
ON dv.document_id = d.id
AND dv.version = (
SELECT MAX(dv2.version)
FROM document_versions dv2
WHERE dv2.document_id = d.id
)
JOIN users u
ON u.id = d.owner_id
WHERE d.slug = $1
AND d.workspace_id = $2;
05 / Get started
The repository contains the SQL schema, this visual preview and the complete data model documentation.