Event Bus e padrão Observer em Rust: implementação e exemplos
Event Bus (barramento de eventos) é um padrão arquitetural que implementa o princípio publish-subscribe (publicador-assinante). Em Rust, esse padrão é especialmente útil para construir sistemas fracamente acoplados, onde os componentes se comunicam através de eventos sem se conhecerem diretamente. Neste artigo, vamos analisar como criar seu próprio Event Bus usando o padrão Observer e explorar soluções prontas do ecossistema Rust.
Padrão Observer: fundamentos
O padrão Observer define uma relação "um-para-muitos" entre objetos. Quando um objeto (publicador) altera seu estado, todos os objetos dependentes (assinantes) são automaticamente notificados. Em Rust, isso é frequentemente implementado através de traits e despacho dinâmico.
Implementação básica do Observer em Rust
use std::collections::HashMap;
// Trait para o observadortrait Observer { fn update(&self, event: &str);}
// Estrutura do publicadorstruct EventManager { observers: Vec<Box<dyn Observer>>,}
impl EventManager { fn new() -> Self { EventManager { observers: Vec::new(), } }
fn subscribe(&mut self, observer: Box<dyn Observer>) { self.observers.push(observer); }
fn notify(&self, event: &str) { for observer in &self.observers { observer.update(event); } }}
// Observador concretostruct Logger;
impl Observer for Logger { fn update(&self, event: &str) { println!("[Logger] Evento recebido: {}", event); }}
fn main() { let mut manager = EventManager::new(); manager.subscribe(Box::new(Logger)); manager.notify("user.login");}Event Bus: versão estendida do Observer
Event Bus é um barramento centralizado que roteia eventos por tipo. Diferente do Observer simples, aqui são suportados diferentes tipos de eventos e filtragem de assinaturas.
Implementação do Event Bus com tipos de eventos
use std::any::Any;use std::collections::HashMap;use std::sync::{Arc, Mutex};
// Tipo de evento#[derive(Clone, Debug)]enum EventType { UserLogin, UserLogout, DataUpdate,}
// Estrutura do evento#[derive(Clone, Debug)]struct Event { event_type: EventType, payload: String,}
// Manipulador de eventotype Handler = Arc<dyn Fn(Event) + Send + Sync>;
// Barramento de eventosstruct EventBus { handlers: HashMap<EventType, Vec<Handler>>,}
impl EventBus { fn new() -> Self { EventBus { handlers: HashMap::new(), } }
fn subscribe<F>(&mut self, event_type: EventType, handler: F) where F: Fn(Event) + 'static + Send + Sync, { self.handlers .entry(event_type) .or_insert_with(Vec::new) .push(Arc::new(handler)); }
fn publish(&self, event: Event) { if let Some(handlers) = self.handlers.get(&event.event_type) { for handler in handlers { handler(event.clone()); } } }}
fn main() { let mut bus = EventBus::new();
bus.subscribe(EventType::UserLogin, |event| { println!("Usuário entrou: {}", event.payload); });
bus.subscribe(EventType::UserLogin, |event| { // Outro manipulador println!("Login registrado: {}", event.payload); });
bus.publish(Event { event_type: EventType::UserLogin, payload: "user123".to_string(), });}Event Bus assíncrono com Tokio
Em aplicações reais, frequentemente é necessário o processamento assíncrono de eventos. Para isso, a biblioteca tokio com seus canais broadcast é excelente.
Uso de tokio::sync::broadcast
use tokio::sync::broadcast;
#[tokio::main]async fn m