Event Bus y el patrón Observer en Rust: implementación y ejemplos
Event Bus (bus de eventos) es un patrón arquitectónico que implementa el principio publish-subscribe (publicador-suscriptor). En Rust, este patrón es especialmente útil para construir sistemas débilmente acoplados, donde los componentes se comunican a través de eventos sin conocerse directamente. En este artículo, analizaremos cómo crear un Event Bus propio utilizando el patrón Observer y revisaremos soluciones listas del ecosistema de Rust.
Patrón Observer: fundamentos
El patrón Observer define una relación «uno-a-muchos» entre objetos. Cuando un objeto (publicador) cambia su estado, todos los objetos dependientes (suscriptores) son notificados automáticamente. En Rust, esto se implementa a menudo mediante traits y despacho dinámico.
Implementación básica de Observer en Rust
use std::collections::HashMap;
// Trait para el observadortrait Observer { fn update(&self, event: &str);}
// Estructura del 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 recibido: {}", event); }}
fn main() { let mut manager = EventManager::new(); manager.subscribe(Box::new(Logger)); manager.notify("user.login");}Event Bus: versión extendida de Observer
Event Bus es un bus centralizado que enruta eventos por tipo. A diferencia del Observer simple, aquí se admiten diferentes tipos de eventos y filtrado de suscripciones.
Implementación de Event Bus con 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,}
// Estructura del evento#[derive(Clone, Debug)]struct Event { event_type: EventType, payload: String,}
// Manejador de eventostype Handler = Arc<dyn Fn(Event) + Send + Sync>;
// Bus 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!("Usuario inició sesión: {}", event.payload); });
bus.subscribe(EventType::UserLogin, |event| { // Otro manejador println!("Inicio de sesión registrado: {}", event.payload); });
bus.publish(Event { event_type: EventType::UserLogin, payload: "user123".to_string(), });}Event Bus asíncrono con Tokio
En aplicaciones reales, a menudo se requiere procesamiento asíncrono de eventos. Para esto, la biblioteca tokio con sus canales broadcast es ideal.
Uso de tokio::sync::broadcast
use tokio::sync::broadcast;
#[tokio::main]async fn m