Event sourcing database schema
An event sourcing schema: aggregates, an append-only event_log partitioned by stream, snapshots for replay performance, and projection state for fast reads.
Database schema previewEvent sourcing database schema
Recreate this in Raltey
Copy this prompt
Paste this into Raltey's AI agent to generate the same diagram, then tweak it on the canvas.
Prompt
Design a PostgreSQL database schema for: Event sourcing database schema. An event sourcing schema: aggregates, an append-only event_log partitioned by stream, snapshots for replay performance, and projection state for fast reads. Include these tables: aggregate, event_log, snapshot, projection_offset. Use proper primary keys, foreign keys, and indexes. Export SQL DDL for PostgreSQL.
Context
- - Single ordered event_log per stream (one row per event).
- - Snapshots periodically materialise an aggregate to reduce replay cost.
- - Read-side projections live in their own tables, kept in sync by a worker.
Patterns
- - event_log primary key (stream_id, version) enforces ordering.
- - Snapshots every N events shorten replay.
- - Projections track their own offset for retry safety.
Tables
The schema, table by table
aggregate
DBML - aggregate
Table aggregate {
id uuid pk
type text not null // order, account, ...
created_at timestamptz default `now()`
}event_log
Append-only ordered event log per aggregate.
DBML - event_log
Table event_log {
stream_id uuid [ref: > aggregate.id, not null]
version int not null
event_type text not null
payload jsonb not null
recorded_at timestamptz default `now()`
pk (stream_id, version)
}snapshot
DBML - snapshot
Table snapshot {
stream_id uuid [ref: > aggregate.id]
version int not null
state jsonb not null
taken_at timestamptz default `now()`
pk (stream_id, version)
}projection_offset
Per-projection cursor into the event log.
DBML - projection_offset
Table projection_offset {
projection_name text pk
last_position bigint not null
updated_at timestamptz default `now()`
}Relationships
| From | To | Type |
|---|---|---|
| aggregate | event_log | 1:N |
| aggregate | snapshot | 1:N |
Notes from the field
- Use optimistic concurrency: insert with the next version; unique key rejects races.
- Partition event_log by month for very high write volume.
- Never UPDATE rows in event_log. Compensating events fix mistakes.
Frequently asked
How often should I snapshot?
A common rule is every 100-1000 events per aggregate, but tune to replay budget. Snapshot if replaying the tail takes more than ~50 ms.
How do I rebuild a projection?
Reset projection_offset.last_position to 0 (or a known checkpoint) and let the worker re-consume from there.
More templates
FlowchartSaaS onboarding email flowFlowchartCustomer support runbook flowFlowchartPayment webhook retry and error flowFlowchartGitHub PR review and merge flowFlowchartLogin with magic link flowArchitectureThree-tier SaaS architecture on AWSArchitectureMicroservices architecture on KubernetesArchitectureReal-time data pipeline on GCP
Open this template in Raltey
Edit it on the canvas, ask the AI agent to extend it, then share or export.
Start for free