Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Evento
A collection of libraries and tools that help you build DDD, CQRS, and event sourcing applications in Rust.
- Event sourcing — state changes stored as immutable events, with optimistic concurrency and a complete audit trail
- CQRS — commands, projections (read models), and continuous subscriptions
- Macros —
#[evento::aggregate],#[evento::command],#[evento::projection],#[evento::snapshot],#[evento::handler],#[evento::subscription] - Compact storage — fast binary serialization with bitcode
One Executor trait, many backends:
| Backend | Feature | Crate | Notes |
|---|---|---|---|
| SQLite / PostgreSQL / MySQL | sqlite / postgres / mysql |
evento-sql | via sqlx, with built-in migrations |
| Fjall (embedded LSM-tree) | fjall |
evento-fjall | no external server |
| Remote (client/server TCP) | remote |
evento-remote | serve any executor over framed TCP |
| Accord (consensus, alpha) | — | evento-accord | leaderless replicated store, strictly serializable (design) |
Installation
Evento 2.x is currently in alpha; pin the exact pre-release version (a bare "2"
does not resolve pre-releases):
[]
= { = "2.0.0-alpha.27", = ["sqlite"] }
= "0.6"
= "1"
Swap sqlite for postgres, mysql, fjall, or remote as needed (see
Feature flags).
Quick start
1. Define events with an aggregate enum
Each variant becomes an event struct with all required traits (bitcode serialization,
Aggregate, AggregateEvent):
The aggregate type defaults to "{package_name}/{enum_name}". Pin it (and event
names) so refactors never orphan stored events:
2. Write events
create() starts a new aggregate; append(id) continues an existing one with
optimistic concurrency:
#
#
# async
3. Build read models with projections
Handlers are pure (event, &mut view) functions; load(id) replays the aggregate's
events through them:
use ;
#
#
// bitcode derives make the view snapshottable through the executor.
async
async
# async
4. Commands and the write gateway
The write side loads current state, guards invariants, then emits events through the
loaded projection's write() gateway — which continues the stream at the version the
load observed, so concurrent commands conflict instead of clobbering each other.
#[evento::command] generates routing-key variants from one method body:
use ;
#
#
// The write model: `id = id` implements ProjectionAggregate (enables `write()`),
// `snapshot(memory)` keeps a process-local materialized row per aggregate.
#
# async
#
# async
;
# async
See examples/bank for the full pattern with domain errors and ten
commands.
5. Snapshots
Loading replays an aggregate's events; snapshots cut that short. Three modes:
- Executor-backed (default): derive
bitcode::Encode/bitcode::Decodeon the projection (#[evento::projection(bitcode::Encode, bitcode::Decode)]) and the snapshot is persisted in the event store. #[evento::snapshot(memory)]: a process-local table keyed by aggregate id, with asnapshot_rows()accessor for reading materialized rows.#[evento::snapshot(none)]: opt out — always replay from scratch.
let rows = snapshot_rows.read.unwrap;
# drop;
6. Subscriptions
Process events continuously (side effects allowed), with cursor tracking, retries, and graceful shutdown:
use ;
#
#
async
# async
To drain currently-pending events once instead of running a background loop, use
run_once(&executor) (optionally after no_retry()). To keep a projection
auto-updated, use projection.subscription("key").start(&executor). Handlers for all
events of an aggregate without deserializing go through #[evento::subscription_all]
with RawEvent<A>.
Wiring a backend
Fjall (embedded, zero setup)
#
SQLite (or PostgreSQL/MySQL) with migrations
use ;
use SqlitePoolOptions;
# async
Remote (client/server split)
Serve any executor over framed TCP; the client implements Executor, so commands,
projections, and subscriptions work unchanged across the network:
# async
Accord (replicated, alpha)
evento-accord replicates writes through the Accord consensus protocol
(Cassandra CEP-15): leaderless, strictly serializable, highly available, with any local
backend (Fjall/SQL) serving reads. See its README,
DESIGN.md, and OPERATIONS.md,
plus the bank-axum-accord 3-node demo.
Core API at a glance
| Concern | Entry point |
|---|---|
| Define events | #[evento::aggregate] enum |
| Start a new aggregate | evento::create() → WriteBuilder |
| Append to an aggregate | evento::append(id) → WriteBuilder |
| Command with routing variants | #[evento::command] impl Command<E> |
| Read model | #[evento::projection] + #[evento::handler] fns |
| Emit events from loaded state | #[evento::projection(id = ...)] → view.write()? |
| Snapshot strategy | bitcode derives / #[evento::snapshot(memory)] / #[evento::snapshot(none)] |
| Load a read model | Projection::new::<A>().handler(..).load(id).execute(exec) |
| Co-keyed secondary aggregate | .load(id).aggregate::<Other>(other_id) |
| Filter events when reading | EventFilter::by_type / by_id / by_event / exact |
| Continuous processing | SubscriptionBuilder::new(key)...start(exec) |
| One-shot processing | SubscriptionBuilder::new(key)...run_once(exec) |
| Keep a projection updated | projection.subscription(key).start(exec) |
| Fail on unhandled events | .strict() |
| Keep going after a handler error | .continue_on_error() |
Full macro reference: evento-macro/README.md.
Feature flags
macro(default) - Procedural macros for aggregates and handlerssql- Enable all SQL database backendssqlite/postgres/mysql- Individual SQL backends with migrationsfjall- Embedded key-value storage with Fjallremote- Client/server executor over framed TCPgroup- Multi-executor support for querying across databasesrw- Read-write split executor for CQRS patterns
Workspace crates
| Crate | Purpose |
|---|---|
| evento | Facade: re-exports core + feature-gated backends |
| evento-core | Executor trait, write path, projections, subscriptions |
| evento-macro | Procedural macros |
| evento-sql | SQLite/MySQL/PostgreSQL executor (sqlx) |
| evento-sql-migrator | Schema migrations for the SQL backend |
| evento-fjall | Embedded LSM-tree executor |
| evento-remote | Client/server executor over framed TCP |
| evento-accord | Accord consensus replicated executor (alpha) |
Examples
Complete working examples in examples/:
quickstart- Smallest end-to-end run (Fjall):cargo run -p quickstartbank- Bank domain: aggregates, ten commands, projections, snapshotsbank-axum-sqlite- Axum + SQLite + migrations:cargo run -p bank-axum-sqlitebank-axum-fjall- Axum + embedded Fjall:cargo run -p bank-axum-fjallbank-axum-remote- Two-process client/server split:cargo run -p bank-axum-remote -- storethencargo run -p bank-axum-remotebank-axum-accord- 1- or 3-node Accord cluster:make accordormake accord.cluster
License
Licensed under the Apache License, Version 2.0.