Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions docs/postgres-event-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Postgres Event Store Contract

This is the storage contract for the future Postgres repository. It narrows the durable representation without requiring the Postgres implementation to land in the same change.

## Event Records

The event table stores aggregate-sourced replay records. Published domain events and integration messages should use the outbox/message tables.

Recommended table shape:

| Column | Type | Notes |
| --- | --- | --- |
| `aggregate_type` | `text` | Stable aggregate type name. |
| `aggregate_id` | `text` | Aggregate stream identifier. |
| `sequence` | `bigint` | One-based stream position. |
| `event_name` | `text` | Stable replay event name. |
| `event_version` | `bigint` | Payload schema version, default `1`. |
| `payload` | `bytea` | Raw encoded event payload bytes. |
| `payload_codec` | `text` | Required codec label, initially `bitcode`. |
| `payload_codec_version` | `integer` | Required codec metadata, initially `1`. |
| `metadata` | `jsonb` | Event metadata, default `{}`. |
| `recorded_at` | `timestamptz` | UTC instant for the event record. |

Required constraints and indexes:

- `PRIMARY KEY (aggregate_type, aggregate_id, sequence)`.
- Optional index `(event_name, event_version)` for migrations or diagnostics.

## Timestamp Representation

Rust `EventRecord::timestamp` remains `SystemTime` in the in-memory API, but Postgres must not persist serde's `SystemTime` JSON shape. The database representation is `recorded_at timestamptz NOT NULL`, bound as a UTC instant. Implementations should round or truncate to the database/driver's supported precision, normally microseconds, and convert back to `SystemTime` at the repository boundary.

## Payload Codec Metadata

Event payload bytes are currently bitcode-encoded. Postgres rows must carry codec metadata beside the payload so future codecs or bitcode compatibility changes can be handled explicitly:

- `payload_codec = 'bitcode'`
- `payload_codec_version = 1`

The repository should reject unknown codec labels or versions unless an explicit decoder/upcaster path exists. Payload schema changes still use `event_version` and aggregate upcasters; codec metadata describes the byte encoding, not the domain event version.

## Backward Compatibility

Rows or imported JSON records without event metadata deserialize with empty metadata. Postgres migrations should still write `metadata jsonb NOT NULL DEFAULT '{}'` so newly stored rows are explicit.
2 changes: 1 addition & 1 deletion docs/research-and-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Approach: Add async versions of the core traits. Could be feature-gated or a par

### Later

- **Postgres backend** — proves the trait design, makes the library production-usable. Use sqlx.
- **Postgres backend** — proves the trait design, makes the library production-usable. Use sqlx. The event-record storage contract is outlined in [Postgres Event Store Contract](postgres-event-store.md).
- **API docs** — `cargo doc` with doc comments on all public traits/types.
- **Publish to crates.io** — after the above items stabilize.
- **Domain service** — TBD whether to keep. Not documenting further until decided.
Expand Down
Loading
Loading