Skip to content

Commit 56607f3

Browse files
authored
Update readme with usage instructions (#40)
1 parent 7e162a9 commit 56607f3

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1111
event-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

15138
This project is licensed under the MIT License - see the [LICENSE](LICENSE)

0 commit comments

Comments
 (0)