Skip to main content
โšก Calmops

Event-Driven Architecture in Rust

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:

  1. Event sourcing - Store all events
  2. CQRS - Separate read/write models
  3. Async messaging - Decouple services
  4. Scalability - Handle high throughput

Comments