Evento - Event Sourcing and CQRS Framework
Evento is a comprehensive library for building event-sourced applications using Domain-Driven Design (DDD) and Command Query Responsibility Segregation (CQRS) patterns in Rust.
Overview
Event sourcing is a pattern where state changes are stored as a sequence of events. Instead of persisting just the current state, you persist all the events that led to the current state. This provides:
- Complete audit trail: Every change is recorded as an event
- Time travel: Replay events to see state at any point in time
- Event-driven architecture: React to events with handlers
- CQRS support: Separate read and write models
Core Concepts
- Events: Immutable facts representing something that happened
- Aggregates: Domain objects that process events and maintain state
- Event Handlers: Functions that react to events and trigger side effects
- Event Store: Persistent storage for events (SQL databases supported)
- Snapshots: Periodic state captures to optimize loading
Quick Start
use evento::prelude::*;
use evento::{EventDetails, AggregatorName};
use serde::{Deserialize, Serialize};
use bincode::{Decode, Encode};
// Define events
#[derive(AggregatorName, Encode, Decode)]
struct UserCreated {
name: String,
email: String,
}
// Define aggregate
#[derive(Default, Serialize, Deserialize, Encode, Decode, Clone, Debug)]
struct User {
name: String,
email: String,
}
// Implement event handlers on the aggregate
#[evento::aggregator]
impl User {
async fn user_created(&mut self, event: EventDetails<UserCreated>) -> anyhow::Result<()> {
self.name = event.data.name;
self.email = event.data.email;
Ok(())
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Setup SQLite executor
let pool = sqlx::SqlitePool::connect("sqlite:events.db").await?;
let executor: evento::Sqlite = pool.into();
// Create and save events
let user_id = evento::create::<User>()
.data(&UserCreated {
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
})?
.metadata(&true)?
.commit(&executor)
.await?;
// Load aggregate from events
let user = evento::load::<User, _>(&executor, &user_id).await?;
println!("User: {:?}", user.item);
Ok(())
}
Features
- SQL Database Support: SQLite, PostgreSQL, MySQL
- Event Handlers: Async event processing with retries
- Event Subscriptions: Continuous event processing
- Streaming: Real-time event streams (with
streamfeature) - Migrations: Database schema management
- Macros: Procedural macros for cleaner code
Feature Flags
macro- Enable procedural macros (default)handler- Enable event handlers (default)stream- Enable streaming supportsql- Enable all SQL database backendssqlite- SQLite supportpostgres- PostgreSQL supportmysql- MySQL supportsql-migrator- Enable database migrations