Event-Driven Architecture in Rust
TL;DR: This guide covers event-driven architecture patterns in Rust. You’ll learn event sourcing, CQRS, message handling, and building reactive systems.
Introduction
Event-driven architecture enables:
- Loose coupling between services
- Scalability and resilience
- Audit trails and replay
- Real-time processing
Event Sourcing
Store Events
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Event {
AccountCreated { account_id: String, owner: String },
MoneyDeposited { account_id: String, amount: f64 },
MoneyWithdrawn { account_id: String, amount: f64 },
TransferCompleted { from: String, to: String, amount: f64 },
}
pub struct EventStore {
events: Vec<Event>,
}
impl EventStore {
pub fn new() -> Self { Self { events: Vec::new() } }
pub fn append(&mut self, event: Event) {
self.events.push(event);
}
pub fn get_events(&self) -> &[Event] {
&self.events
}
}
CQRS Pattern
Command Side
pub struct CommandHandler {
event_store: Arc<RwLock<EventStore>>,
}
impl CommandHandler {
pub fn create_account(&self, id: String, owner: String) -> Result<(), Error> {
let event = Event::AccountCreated { account_id: id, owner };
self.event_store.write().unwrap().append(event);
Ok(())
}
}
Query Side
pub struct QueryHandler {
projections: Arc<RwLock<HashMap<String, Account>>>,
}
impl QueryHandler {
pub fn get_account(&self, id: &str) -> Option<Account> {
self.projections.read().unwrap().get(id).cloned()
}
}
Message Handling
Async Message Processing
use tokio::sync::mpsc;
pub async fn process_messages() {
let (tx, mut rx) = mpsc::channel::<Message>(100);
// Producer
tokio::spawn(async move {
let msg = Message { payload: "data".into() };
tx.send(msg).await.unwrap();
});
// Consumer
while let Some(msg) = rx.recv().await {
process(msg).await;
}
}
Conclusion
Event-driven architecture provides:
- Event sourcing - Store all events
- CQRS - Separate read/write models
- Async messaging - Decouple services
- Scalability - Handle high throughput
Comments