Skip to content
Menu

Documentation

Database

The database is your system of record: it backs the unit of work, the outbox and audit.

What a module calls

db() is the session of the running unit on the module's database (Units of work). A named database is Annotated[AsyncSession, Database("x")] in a route, or open_uow("x") in a block. Each database has its own unit and its own outbox, and no transaction spans two of them. A route that writes to several needs writes(...) or allow_multi_db_write(...) (Write admission).

Drivers

sqlalchemy

PostgreSQL through asyncpg, and the only relational backend. Set DATABASE_URL. The URL must name its driver, postgresql+asyncpg://; never rely on a bare postgresql://.

Option Meaning
url the connection URL; falls back to DATABASE_URL
tenant_rls turns on PostgreSQL row-level security (Multi-tenancy)
system_url the separate login role for framework cross-tenant work; needs tenant_rls
require_system_channel fails startup when no system channel is configured

The relational paradigm

database is the relational capability: the SQL-shaped system of record that backs the unit of work, the outbox and audit. The framework abstracts engine construction and pooling; the query dialect is SQLAlchemy, used only in models/ and services/. Sibling paradigms (document_store, graph_store, ...) have their own contracts and weaker guarantees, so no universal "database" abstraction has to span them. The relational guarantees are features (transactional_outbox, audit), not assumptions: a driver that cannot provide them does not advertise them. No distributed transaction spans backends.

The relational backend is not freely swappable today: the only driver is sqlalchemy, the unit of work, outbox, audit, tenancy and migrations are SQLAlchemy code in the kernel, and the settings require a postgresql+asyncpg:// URL. In practice it is PostgreSQL.

Sandloom isolates business code from connecting to and operating infrastructure; the full set of transactional guarantees is implemented and verified on PostgreSQL through the SQLAlchemy driver. The unit guarantees (one writer per request, SANDLOOM-R007; conservative write detection at the statement boundary; refusal of an exited unit, SANDLOOM-R002; the lazy request unit; unflushed changes counted as writes) are driver obligations, not kernel code. A database driver advertises them as the write_admission feature (with transactions), implements LazyUnitOfWorkContract (open_lazily / close) and DatabaseProvider.has_pending_changes, and create_app refuses (SANDLOOM-B010) a database that hosts request units without write_admission. Another relational driver must implement these features to host request units; a driver conformance suite for them is planned.