@@ -10,6 +10,129 @@ of your domain events. It is built on top of [Kurrent](https://kurrent.dev/)
1010(formerly EventStoreDB) and provides a clean, type-safe API for implementing
1111event-sourced systems.
1212
13+ ## Getting Started
14+
15+ ### Installation
16+
17+ Add Mneme to your ` Cargo.toml ` :
18+
19+ ``` toml
20+ [dependencies ]
21+ mneme = " 0.1.0"
22+ ```
23+
24+ ### Basic Concepts
25+
26+ Mneme implements the event sourcing pattern with these core components:
27+
28+ - ** Commands** : Operations that may produce events
29+ - ** Events** : Facts that have happened in the system
30+ - ** State** : Derived from applying events sequentially
31+ - ** Event Store** : Persists the event stream for each aggregate
32+
33+ ### Usage Example
34+
35+ ``` rust
36+ use mneme :: {AggregateState , Command , Event , EventStore , EventStreamId , execute};
37+ use serde :: {Deserialize , Serialize };
38+ use uuid :: Uuid ;
39+
40+ // 1. Define your events
41+ #[derive(Debug , Clone , Deserialize , Serialize )]
42+ enum BankAccountEvent {
43+ Created { id : Uuid , owner : String },
44+ Deposited { id : Uuid , amount : u32 },
45+ Withdrawn { id : Uuid , amount : u32 }
46+ }
47+
48+ impl Event for BankAccountEvent {
49+ fn event_type (& self ) -> String {
50+ match self {
51+ BankAccountEvent :: Created { .. } => " BankAccount.Created" . to_string (),
52+ BankAccountEvent :: Deposited { .. } => " BankAccount.Deposited" . to_string (),
53+ BankAccountEvent :: Withdrawn { .. } => " BankAccount.Withdrawn" . to_string (),
54+ }
55+ }
56+ }
57+
58+ // 2. Define your aggregate state
59+ #[derive(Clone , Debug )]
60+ struct AccountState {
61+ balance : u32 ,
62+ }
63+
64+ impl AggregateState <BankAccountEvent > for AccountState {
65+ fn apply (& mut self , event : & BankAccountEvent ) -> & Self {
66+ match event {
67+ BankAccountEvent :: Created { .. } => {},
68+ BankAccountEvent :: Deposited { amount , .. } => {
69+ self . balance += amount ;
70+ },
71+ BankAccountEvent :: Withdrawn { amount , .. } => {
72+ self . balance -= amount ;
73+ }
74+ }
75+ self
76+ }
77+ }
78+
79+ // 3. Define a command
80+ #[derive(Clone )]
81+ struct WithdrawCommand {
82+ id : Uuid ,
83+ amount : u32 ,
84+ state : AccountState ,
85+ }
86+
87+ impl Command for WithdrawCommand {
88+ type Event = BankAccountEvent ;
89+ type State = AccountState ;
90+ type Error = String ;
91+
92+ fn get_state (& self ) -> Self :: State {
93+ self . state. clone ()
94+ }
95+
96+ fn set_state (& mut self , state : & Self :: State ) {
97+ self . state = state . clone ();
98+ }
99+
100+ fn event_stream_id (& self ) -> EventStreamId {
101+ EventStreamId (self . id)
102+ }
103+
104+ fn handle (& self ) -> Result <Vec <Self :: Event >, Self :: Error > {
105+ if self . amount <= self . state. balance {
106+ Ok (vec! [BankAccountEvent :: Withdrawn {
107+ id : self . id,
108+ amount : self . amount
109+ }])
110+ } else {
111+ Err (" Insufficient funds" . to_string ())
112+ }
113+ }
114+ }
115+
116+ // 4. Use the execute function with your event store
117+ async fn process_withdrawal (account_id : Uuid , amount : u32 ) -> Result <(), mneme :: Error > {
118+ let mut event_store = /* your event store implementation */ ;
119+
120+ let command = WithdrawCommand {
121+ id : account_id ,
122+ amount ,
123+ state : AccountState { balance : 0 }, // Initial state will be replaced by stored events
124+ };
125+
126+ execute (command , & mut event_store , Default :: default ()). await
127+ }
128+ ```
129+
130+ ## Advanced Features
131+
132+ - ** Optimistic Concurrency** : Handles concurrent updates to the same event stream
133+ - ** State Reconstruction** : Automatically rebuilds aggregate state from event history
134+ - ** Type Safety** : Leverages Rust's type system for safe event handling
135+
13136## License
14137
15138This project is licensed under the MIT License - see the [ LICENSE] ( LICENSE )
0 commit comments