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