Skip to main content

eventfold_es/
lib.rs

1//! Embedded event-sourcing framework built on top of [`eventfold`].
2//!
3//! `eventfold-es` provides the building blocks for event-sourced applications:
4//! aggregates, projections, process managers, and a typed command bus. All
5//! state is persisted to disk via `eventfold`'s append-only JSONL logs --
6//! no external database required.
7//!
8//! # Key Types
9//!
10//! | Type | Role |
11//! |------|------|
12//! | [`Aggregate`] | Domain model: handles commands, emits events, folds state |
13//! | [`AggregateStore`] | Central registry: spawns actors, caches handles, runs projections |
14//! | [`Projection`] | Cross-stream read model built from events |
15//! | [`ProcessManager`] | Cross-aggregate workflow that reacts to events with commands |
16//! | [`CommandBus`] | Typed command router keyed by `TypeId` |
17//! | [`AggregateHandle`] | Async handle to a running aggregate actor |
18//!
19//! # Quick Start
20//!
21//! ```no_run
22//! use eventfold_es::{
23//!     Aggregate, AggregateStore, CommandContext,
24//! };
25//! use serde::{Deserialize, Serialize};
26//!
27//! // 1. Define your aggregate.
28//! #[derive(Debug, Clone, Default, Serialize, Deserialize)]
29//! struct Counter { value: u64 }
30//!
31//! #[derive(Debug, Clone, Serialize, Deserialize)]
32//! #[serde(tag = "type", content = "data")]
33//! enum CounterEvent { Incremented }
34//!
35//! #[derive(Debug, thiserror::Error)]
36//! enum CounterError {}
37//!
38//! impl Aggregate for Counter {
39//!     const AGGREGATE_TYPE: &'static str = "counter";
40//!     type Command = String;  // simplified for example
41//!     type DomainEvent = CounterEvent;
42//!     type Error = CounterError;
43//!
44//!     fn handle(&self, _cmd: String) -> Result<Vec<CounterEvent>, CounterError> {
45//!         Ok(vec![CounterEvent::Incremented])
46//!     }
47//!     fn apply(mut self, _event: &CounterEvent) -> Self {
48//!         self.value += 1;
49//!         self
50//!     }
51//! }
52//!
53//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
54//! // 2. Open the store and send commands.
55//! let store = AggregateStore::open("/tmp/my-app").await?;
56//! let handle = store.get::<Counter>("counter-1").await?;
57//! handle.execute("go".into(), CommandContext::default()).await?;
58//!
59//! let state = handle.state().await?;
60//! assert_eq!(state.value, 1);
61//! # Ok(())
62//! # }
63//! ```
64//!
65//! See `examples/counter.rs` for a self-contained runnable example that
66//! demonstrates aggregates, projections, and the command bus.
67
68mod actor;
69pub use actor::{AggregateHandle, spawn_actor};
70mod aggregate;
71pub use aggregate::{Aggregate, reducer, to_eventfold_event};
72mod command;
73mod error;
74mod process_manager;
75mod projection;
76mod storage;
77mod store;
78
79pub use command::{CommandBus, CommandContext, CommandEnvelope};
80pub use error::{DispatchError, ExecuteError, StateError};
81pub use process_manager::{ProcessManager, ProcessManagerReport};
82pub use projection::Projection;
83pub use storage::StreamLayout;
84pub use store::{AggregateStore, AggregateStoreBuilder, InjectOptions};