Expand description
turnframe-store: the persistence contract of Turnframe, plus a
deterministic in-memory implementation and an executable conformance suite.
It defines what must be durable and with which rules, as seven
object-safe traits, and nothing about where: no driver, no SQL. The
PostgreSQL adapter is one implementation and is optional. The rules an
implementation must honour — total account scoping, a closed error surface,
compare-and-swap instead of blind overwrite, immutability, specified
ordering — and the all-or-nothing commit model are in
docs/persistence.md.
conformance::run_all proves an implementation right without reading its
code, and MemoryStores is the reference one.
Six traits are split into a …Reader and a …Writer half, aggregated by
the trait carrying the historical name, so a caller that must not write can
be handed ReadOnlyStores and the compiler keeps it that way.
Stores bundles one implementation of each.
| Trait | Module | What it owns |
|---|---|---|
ConversationStore | conversation | conversations, user turns, assistant turns as returned, the crash-recovery phase marker |
InteractionStore | interaction | persisted cards: payloads, the one-blocking-per-case slot, compare-and-swap resolution, expiry |
CommandJournal | journal | idempotency admission and the persisted outcome of every command |
EventJournal | events | the append-only claim ledger, paged exactly once, erasable only by redaction |
OutboxStore | outbox | external side effects awaiting dispatch, with claim/reschedule semantics |
ReplayStore | replay | one replay record per turn, upserted as the turn advances |
CommitStore | commit | the all-or-nothing write of everything one commit produces |
§Example
use turnframe_core::ids::{AccountId, ConversationId};
use turnframe_store::prelude::*;
let stores = Stores::in_memory();
let account = AccountId::from("aurora");
let conversation = ConversationId::new();
let now = chrono::Utc::now();
stores
.conversations()
.create_conversation(ConversationRecord::new(
conversation,
account.clone(),
now,
))
.await?;
let loaded = stores
.conversations()
.load_conversation(&account, &conversation)
.await?;
assert_eq!(loaded.account_id, account);
// Another tenant cannot tell it apart from one that never existed.
let other = AccountId::from("other");
assert_eq!(
stores
.conversations()
.load_conversation(&other, &conversation)
.await,
Err(StoreError::NotFound)
);Re-exports§
pub use crate::error::StoreResult;pub use crate::memory::Clock;pub use crate::memory::ManualClock;pub use crate::memory::MemoryStores;pub use crate::memory::SystemClock;pub use crate::stores::ReadOnlyStores;pub use crate::stores::ReadOnlyStoresBuilder;pub use crate::stores::Stores;pub use crate::stores::StoresBuilder;
Modules§
- commit
- The atomicity model: one
CommitBundle, one all-or-nothing write (spec §16.3, §23 step N, ADR-006 point 8). - conformance
- An executable statement of the persistence contract.
- conversation
- Conversations, persisted turns and the crash-recovery phase marker (spec §22.3, §23.1).
- error
- The error surface of the persistence contract.
- events
- The event journal: the append-only claim ledger (spec §17.1, ADR-012).
- interaction
- Persistent interactions: immutable payloads, the one-blocking-per-case slot and compare-and-swap resolution (spec §15.5, §15.6).
- journal
- The command journal: idempotency admission and persisted outcomes (spec §16.2, §23.1, I14).
- memory
MemoryStores: one deterministic implementation of every persistence trait, over a single shared state.- outbox
- The outbox: external side effects awaiting dispatch (spec §16.4, ADR-007).
- prelude
- The items an application needs to hold and use a set of stores.
- replay
- Replay records: one per turn, rewritten as the turn progresses (spec §23.1, I20).
- stores
Stores: one value carrying an implementation of every persistence trait.
Enums§
- Store
Error - Errors raised by persistence adapters.