Describe the domain.
Serve it over HTTP and MCP.
Fluvius is a Python 3.12+ framework for domain-driven backends. You write commands, aggregates, and queries. Fluvius records every change in an event log and serves the domain through FastAPI and MCP.
fluvius.fastapi · fluvius.mcp
Compose the app, one pipe at a time
Configurators take the app and return it, so ordering is explicit. Switch a line off to see what the app stops serving.
app = ()
- HTTP/api/docsOpenAPI UI
- HTTP/healthFramework health and readiness
- HTTP/api/authKeycloak / OpenID Connect sign-in, sessions, CSRF
- HTTP/api/user-profile/create-user:post/userCommand endpoint
- HTTP/api/user-profile/update-user:exec/user/{id}Command endpoint
- HTTP/api/user-profile/invalidate-user:exec/user/{id}Command endpoint
6 HTTP surfaces · 0 MCP endpoint
fluvius.domain · fluvius.data · fluvius.query
Build a domain in six files
The user-profile example from the docs, in the order you would write it.
import sqlalchemy as safrom fluvius.data import SqlaDataSchema, SqlaDriver, DataAccessManagerfrom sqlalchemy.dialects import postgresql as pgfrom fluvius.data import UUID_GENRclass UserConnector(SqlaDriver):__db_dsn__ = "postgresql+asyncpg://postgres:postgres@localhost:5432/postgres"class User(UserConnector.__data_schema_base__):__tablename__ = "user"_id = sa.Column(pg.UUID, primary_key=True, default=UUID_GENR)name = sa.Column(sa.String)_created = sa.Column(sa.DateTime(timezone=True))_updated = sa.Column(sa.DateTime(timezone=True))_etag = sa.Column(sa.String)_deleted = sa.Column(sa.DateTime(timezone=True))_creator = sa.Column(pg.UUID)_updater = sa.Column(pg.UUID)_realm = sa.Column(sa.String)class FluviusAccessManager(DataAccessManager):__connector__ = UserConnector__automodel__ = True
fluvius.domain
What happens to a command
A call becomes a command, the aggregate applies it, and the result is appended to the event log. Scrub the log to replay one user.
- CallHTTP · MCPA request arrives through FastAPI or an MCP tool call.
- Commandcreate-userCreateUserCmd validates the payload against its Data model.
- AggregateUserAggregate.create_userThe @action decorator names the event and applies the change.
- Event loguser-createdSQLDomainLogStore appends the event to the domain log.
- QueryUserQueryThe read model serves the new state through typed fields.
- _id
- 7f3a91c2
- name
- "Ada King"
- _created
- 09:41:07
- _updated
- 10:15:40
- _deleted
- 10:15:40
The event log is the history. Replay it to see the state at any point.
What ships in the box
The parts most backends rebuild by hand, already wired to the same domain.
Access
Operations
History
Background and realtime
Agents
Install the core. Add packages when you need them.
Model
Serve
Optional dependency groups
python -m pip install "fluvius[workq]"Redis, NATS, and PostgreSQL Taskiq brokerspython -m pip install "fluvius[transport]"MQTT and NATS transport clientspython -m pip install "fluvius[hatchet]"Hatchet workflow integration
Five minutes to a running API
- 1Install
python -m pip install fluvius - 2Compose the app
app = create_app(pkginfo) | configure_routes() - 3Run it
uvicorn myapp.main:app --reloadOpen /docs for OpenAPI and /health for framework health.
Start with the quick start.