Factos Sqlight
factos_sqlight stores Factos events in SQLite through
sqlight. It is intended for embedded,
single-process applications that want the same model and decision-context API as
other Factos backends.
Setup
Apply priv/migrations.sql, or copy it into your application migration history.
The schema contains one append-only factos_events table.
Configure and dispatch
let model = user.model()
let configuration = factos_sqlight.configure(model, connection: connection)
configuration
|> factos_sqlight.dispatch(
command,
decision_context: user.decision_context(command),
event_id: new_event_id,
)
Dispatch uses BEGIN IMMEDIATE, so the context read, event append, and
subscriptions share one SQLite transaction.
Transactional subscriptions
let projection =
factos.subscription(fn(connection, recorded) {
use _ <- result.try(projection.apply(connection, recorded))
Ok(connection)
})
let configuration =
factos_sqlight.Configuration(
..factos_sqlight.configure(model, connection: connection),
subscriptions: [projection],
)
A subscription failure rolls back the event append and all subscription writes.
Simulate projections
Use factos/factos_sqlight/simulate to run the same subscriptions from an
in-memory domain scenario against a real SQLite projection database:
import factos/factos_sqlight/simulate as simulate_sqlight
import factos/simulate
let simulation =
simulate_sqlight.new(
model,
with: existing_events,
connection:,
subscriptions: [projection],
)
|> simulate.dispatch(decision_context:, command:)
assert simulate.errors(simulation) == []
assert Ok(rows) = projection.load(connection)
The initial event batch and each accepted dispatch batch run in their own
BEGIN IMMEDIATE transaction. Subscription or transaction failure rolls back
projection writes and leaves the batch out of simulated history. The simulator
does not write the SQLite event log; event-store atomicity and locking remain
dispatch integration concerns.
Durable work
There is no built-in M25 equivalent for SQLite. Persist application-owned jobs through a transactional subscription, then process them after commit with a supervised worker. External IO must not run inside a retryable dispatch.
Recovery reads
read_after returns a bounded page in global position order for rebuilds and
application-owned recovery.
Development
trellis run test factos_sqlight
See How factos_sqlight works for locking and retry details.