PostgreSQL Store
First-class database-backed storage for applications that already run on PostgreSQL. The PostgreSQL store persists facts, committed batches, and durable stream cursors with the same FACTSTR semantics as the other stores.
FACTSTR is an open-source event store built in Rust for applications that use business facts as the source of truth. Store, retrieve, and stream your facts with ease.
FACTSTR gives you the event-store mechanics for facts-first event sourcing without forcing an aggregate-first model.
Record one or more facts as one committed batch with one consecutive sequence range.
Read facts by event type and payload predicates, so commands and projections can load the facts they need.
Append new facts only when the relevant fact context is still current.
Subscribe to facts after they are successfully recorded, with committed batches delivered in order.
Resume stream processing from a stored cursor and catch up without skipping committed facts.
Use Memory, SQLite, or PostgreSQL behind the same event-store contract.
Use the Rust implementation directly or call it from Node.js with TypeScript types.
FACTSTR is easy to integrate and simple to use. A feature can append facts, read the facts it needs, check its context safely, and keep its own query model current from committed batches. That lets a system start small, stay clear, and grow feature by feature.
Example: initialize a PostgreSQL store in Rust
use factstr_postgres::PostgresStore;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let database_url =
std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let _store = PostgresStore::connect(&database_url)?;
Ok(())
}
A feature can update its own query model from committed batches, and durable streams can resume from a stored cursor to replay what was missed before continuing live. That supports feature-owned projections, replay, and catch-up in the same model.
A feature can keep its own query model current from the facts it cares about.
Streams deliver committed batches only after append succeeds.
Durable streams replay what was missed and then continue with future committed batches.
FACTSTR is built for starting with a real feature instead of designing a large structure first. A feature can append facts, read the relevant facts, check its context safely, and keep its own query model current from committed batches.
That keeps the model direct and lets a system grow feature by feature.
Facts are appended to one ordered log, which keeps history and replay simple.
`append_if` checks the relevant context directly, so safety stays close to the feature.
Reads return a position for what was returned, and a separate version for the full relevant context.
Committed batches can keep feature-owned query models current, and durable streams add replay and catch-up from a stored cursor.
FACTSTR has one event-store contract with three runtime stores.
First-class database-backed storage for applications that already run on PostgreSQL. The PostgreSQL store persists facts, committed batches, and durable stream cursors with the same FACTSTR semantics as the other stores.
Embedded persistence in one local database file. The SQLite store keeps facts, committed batches, and durable stream cursors available across restart without requiring a separate database server.
Fast feedback for tests, examples, and local development. The memory store is the simplest way to exercise FACTSTR behavior without persistence or external infrastructure.
Append, query, conditional append, streams, and durable streams keep the same observable behavior across PostgreSQL, SQLite, and Memory.
FACTSTR keeps the important behavior explicit: reads are ordered, `append_if` checks the relevant context, committed batches are delivered only after append succeeds, and durable streams replay from a stored cursor before continuing live.
last_returned_sequence_number and current_context_version are separate on purpose.append_if checks the full relevant context and returns a typed conflict on stale context.FACTSTR is the current Rust implementation of the same general direction explored earlier in TypeScript. These links are here for readers who want the longer background.
Rico Fritzsche
Ralf Westphal
Rico Fritzsche
Rico Fritzsche