eventastic/
lib.rs

1//! # Eventastic
2//!
3//! A type-safe event sourcing and CQRS library for Rust with PostgreSQL persistence.
4//!
5//! Eventastic provides strong consistency guarantees through mandatory transactions,
6//! built-in idempotency checking, and reliable side effect processing via the
7//! transactional outbox pattern.
8//!
9//! ## Quick Start
10//!
11//! ```rust
12//! use eventastic::aggregate::{Aggregate, Context, Root, SideEffect};
13//! use eventastic::event::DomainEvent;
14//! use eventastic::memory::InMemoryRepository;
15//! use eventastic::repository::Repository;
16//!
17//! // Define your domain aggregate
18//! #[derive(Clone, Debug)]
19//! struct Counter {
20//!     id: String,
21//!     value: i32,
22//! }
23//!
24//! // Define your domain events
25//! #[derive(Clone, Debug, PartialEq, Eq)]
26//! enum CounterEvent {
27//!     Created { event_id: String, initial_value: i32 },
28//!     Incremented { event_id: String, amount: i32 },
29//! }
30//!
31//! impl DomainEvent for CounterEvent {
32//!     type EventId = String;
33//!     fn id(&self) -> &Self::EventId {
34//!         match self {
35//!             CounterEvent::Created { event_id, .. } => event_id,
36//!             CounterEvent::Incremented { event_id, .. } => event_id,
37//!         }
38//!     }
39//! }
40//!
41//! // Define side effects (optional)
42//! #[derive(Clone, Debug, PartialEq, Eq)]
43//! struct NoSideEffect {
44//!     id: String,
45//! }
46//!
47//! impl SideEffect for NoSideEffect {
48//!     type SideEffectId = String;
49//!     fn id(&self) -> &Self::SideEffectId {
50//!         &self.id
51//!     }
52//! }
53//!
54//! // Implement the Aggregate trait
55//! impl Aggregate for Counter {
56//!     const SNAPSHOT_VERSION: u64 = 1;
57//!     type AggregateId = String;
58//!     type DomainEvent = CounterEvent;
59//!     type ApplyError = String;
60//!     type SideEffect = NoSideEffect;
61//!
62//!     fn aggregate_id(&self) -> &Self::AggregateId {
63//!         &self.id
64//!     }
65//!
66//!     fn apply_new(event: &Self::DomainEvent) -> Result<Self, Self::ApplyError> {
67//!         match event {
68//!             CounterEvent::Created { initial_value, .. } => Ok(Counter {
69//!                 id: "counter-1".to_string(),
70//!                 value: *initial_value,
71//!             }),
72//!             _ => Err("Counter must be created first".to_string()),
73//!         }
74//!     }
75//!
76//!     fn apply(&mut self, event: &Self::DomainEvent) -> Result<(), Self::ApplyError> {
77//!         match event {
78//!             CounterEvent::Created { .. } => Err("Counter already created".to_string()),
79//!             CounterEvent::Incremented { amount, .. } => {
80//!                 self.value += amount;
81//!                 Ok(())
82//!             }
83//!         }
84//!     }
85//!
86//!     fn side_effects(&self, _event: &Self::DomainEvent) -> Option<Vec<Self::SideEffect>> {
87//!         None
88//!     }
89//! }
90//!
91//! // Usage with in-memory repository
92//! # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
93//! let repository = InMemoryRepository::<Counter>::new();
94//!
95//! // Create and persist a new counter
96//! let mut counter: Context<Counter> = Counter::record_new(
97//!     CounterEvent::Created {
98//!         event_id: "evt-1".to_string(),
99//!         initial_value: 0,
100//!     }
101//! )?;
102//!
103//! counter.record_that(CounterEvent::Incremented {
104//!     event_id: "evt-2".to_string(),
105//!     amount: 5,
106//! })?;
107//!
108//! // Save to repository
109//! let mut transaction = repository.begin_transaction().await?;
110//! transaction.store(&mut counter).await?;
111//! transaction.commit()?;
112//!
113//! // Load from repository
114//! let loaded_counter = repository.load(&"counter-1".to_string()).await?;
115//! assert_eq!(loaded_counter.state().value, 5);
116//! assert_eq!(loaded_counter.version(), 1);
117//! # Ok(())
118//! # }
119//! ```
120//!
121//! ## Architecture
122//!
123//! Eventastic is built around four core modules:
124//!
125//! - **[`aggregate`]** - Domain aggregates that encapsulate business logic and generate events
126//! - **[`event`]** - Domain events and event store abstractions for persistence
127//! - **[`repository`]** - Transaction-based persistence layer with read/write operations
128//! - **[`memory`]** - In-memory implementation for testing and development
129//!
130//! ### Transaction-First Design
131//!
132//! Unlike many event sourcing libraries, Eventastic requires transactions for all write
133//! operations. This ensures:
134//!
135//! - **ACID compliance** - All changes are atomic and consistent
136//! - **Idempotency** - Duplicate events are detected and handled gracefully
137//! - **Side effect reliability** - External operations are processed via outbox pattern
138//! - **Optimistic concurrency** - Concurrent modifications are detected and rejected
139//!
140//! ## Complete Example
141//!
142//! For an implementation demonstrating all concepts, see the
143//! [banking example](https://github.com/jdon/eventastic/tree/main/examples/bank)
144//! which shows.
145
146pub mod aggregate;
147pub mod event;
148pub mod repository;
149
150pub mod memory;
151
152#[cfg(test)]
153mod test_fixtures;