use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::{broadcast, RwLock};
use chrono::{DateTime, Utc};
use crate::{DomainEvent, EventEnvelope, EventError, EventHandler};
type HandlerMap<E> = Arc<RwLock<HashMap<String, Vec<Arc<dyn EventHandler<E>>>>>>;
#[derive(Debug, Clone)]
pub struct EventBusConfig {
pub buffer_size: usize,
pub persist_events: bool,
pub retention_seconds: u64,
pub max_history_size: usize,
}
impl Default for EventBusConfig {
fn default() -> Self {
Self {
buffer_size: 1000,
persist_events: false,
retention_seconds: 86400, max_history_size: 10000,
}
}
}
impl EventBusConfig {
pub fn with_persistence() -> Self {
Self {
persist_events: true,
..Default::default()
}
}
pub fn buffer_size(mut self, size: usize) -> Self {
self.buffer_size = size;
self
}
pub fn retention_seconds(mut self, seconds: u64) -> Self {
self.retention_seconds = seconds;
self
}
}
pub struct EventBus<E: DomainEvent> {
sender: broadcast::Sender<EventEnvelope<E>>,
config: EventBusConfig,
handlers: HandlerMap<E>,
history: Arc<RwLock<Vec<EventEnvelope<E>>>>,
}
impl<E: DomainEvent> EventBus<E> {
pub fn new() -> Self {
Self::with_config(EventBusConfig::default())
}
pub fn with_config(config: EventBusConfig) -> Self {
let (sender, _) = broadcast::channel(config.buffer_size);
Self {
sender,
config,
handlers: Arc::new(RwLock::new(HashMap::new())),
history: Arc::new(RwLock::new(Vec::new())),
}
}
pub async fn publish(&self, event: E) -> Result<(), EventError> {
let envelope = EventEnvelope::new(event);
self.publish_envelope(envelope).await
}
pub async fn publish_all(&self, events: Vec<E>) -> Result<(), EventError> {
for event in events {
self.publish(event).await?;
}
Ok(())
}
pub async fn publish_envelope(&self, envelope: EventEnvelope<E>) -> Result<(), EventError> {
if self.config.persist_events {
self.store_event(&envelope).await;
}
let _ = self.sender.send(envelope.clone());
self.dispatch(envelope).await?;
Ok(())
}
pub async fn register_handler(&self, handler: Arc<dyn EventHandler<E>>) {
let event_types = handler.event_types();
let mut handlers = self.handlers.write().await;
if event_types.is_empty() {
handlers
.entry("*".to_string())
.or_default()
.push(Arc::clone(&handler));
} else {
for event_type in event_types {
handlers
.entry(event_type.to_string())
.or_default()
.push(Arc::clone(&handler));
}
}
}
pub fn subscribe(&self) -> broadcast::Receiver<EventEnvelope<E>> {
self.sender.subscribe()
}
pub async fn history(&self) -> Vec<EventEnvelope<E>> {
self.history.read().await.clone()
}
pub async fn events_for_aggregate(&self, aggregate_id: &str) -> Vec<EventEnvelope<E>> {
self.history
.read()
.await
.iter()
.filter(|e| e.aggregate_id == aggregate_id)
.cloned()
.collect()
}
pub async fn events_by_type(&self, event_type: &str) -> Vec<EventEnvelope<E>> {
self.history
.read()
.await
.iter()
.filter(|e| e.event_type == event_type)
.cloned()
.collect()
}
pub async fn events_in_range(
&self,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Vec<EventEnvelope<E>> {
self.history
.read()
.await
.iter()
.filter(|e| e.occurred_at >= start && e.occurred_at <= end)
.cloned()
.collect()
}
pub async fn clear_history(&self) {
self.history.write().await.clear();
}
pub async fn handler_count(&self) -> usize {
self.handlers
.read()
.await
.values()
.map(|v| v.len())
.sum()
}
async fn store_event(&self, envelope: &EventEnvelope<E>) {
let mut history = self.history.write().await;
history.push(envelope.clone());
let cutoff = Utc::now() - chrono::Duration::seconds(self.config.retention_seconds as i64);
history.retain(|e| e.published_at > cutoff);
while history.len() > self.config.max_history_size {
history.remove(0);
}
}
async fn dispatch(&self, envelope: EventEnvelope<E>) -> Result<(), EventError> {
let handlers = self.handlers.read().await;
let mut handlers_to_call = Vec::new();
if let Some(type_handlers) = handlers.get(envelope.event_type) {
handlers_to_call.extend(type_handlers.iter().cloned());
}
if let Some(wildcard_handlers) = handlers.get("*") {
handlers_to_call.extend(wildcard_handlers.iter().cloned());
}
for handler in handlers_to_call {
if let Err(e) = handler.handle(envelope.clone()).await {
tracing::error!(
handler = %handler.name(),
event_type = %envelope.event_type,
error = ?e,
"Event handler error"
);
}
}
Ok(())
}
}
impl<E: DomainEvent> Default for EventBus<E> {
fn default() -> Self {
Self::new()
}
}
impl<E: DomainEvent> Clone for EventBus<E> {
fn clone(&self) -> Self {
Self {
sender: self.sender.clone(),
config: self.config.clone(),
handlers: Arc::clone(&self.handlers),
history: Arc::clone(&self.history),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::handler::CollectingHandler;
#[derive(Clone, Debug)]
struct TestEvent {
id: String,
message: String,
}
impl DomainEvent for TestEvent {
fn event_type(&self) -> &'static str {
"TestEvent"
}
fn aggregate_id(&self) -> &str {
&self.id
}
}
#[tokio::test]
async fn test_event_bus_publish() {
let bus = EventBus::<TestEvent>::with_config(EventBusConfig::with_persistence());
let event = TestEvent {
id: "test-123".to_string(),
message: "Hello".to_string(),
};
bus.publish(event).await.unwrap();
let history = bus.history().await;
assert_eq!(history.len(), 1);
assert_eq!(history[0].event_type, "TestEvent");
}
#[tokio::test]
async fn test_event_bus_subscribe() {
let bus = EventBus::<TestEvent>::new();
let mut rx = bus.subscribe();
let event = TestEvent {
id: "test-123".to_string(),
message: "Hello".to_string(),
};
bus.publish(event).await.unwrap();
let envelope = rx.recv().await.unwrap();
assert_eq!(envelope.event_type, "TestEvent");
assert_eq!(envelope.aggregate_id, "test-123");
}
#[tokio::test]
async fn test_event_bus_handler() {
let bus = EventBus::<TestEvent>::new();
let handler = Arc::new(CollectingHandler::<TestEvent>::new());
bus.register_handler(handler.clone()).await;
let event = TestEvent {
id: "test-123".to_string(),
message: "Hello".to_string(),
};
bus.publish(event).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
assert_eq!(handler.count().await, 1);
}
#[tokio::test]
async fn test_events_for_aggregate() {
let bus = EventBus::<TestEvent>::with_config(EventBusConfig::with_persistence());
bus.publish(TestEvent {
id: "agg-1".to_string(),
message: "First".to_string(),
})
.await
.unwrap();
bus.publish(TestEvent {
id: "agg-1".to_string(),
message: "Second".to_string(),
})
.await
.unwrap();
bus.publish(TestEvent {
id: "agg-2".to_string(),
message: "Other".to_string(),
})
.await
.unwrap();
let events = bus.events_for_aggregate("agg-1").await;
assert_eq!(events.len(), 2);
}
}