pub trait EventRouter {
fn resolve_topic(&self, event_type: &str) -> crate::HexResult<std::string::String>;
fn resolve_subject(&self, aggregate_id: &str) -> std::option::Option<std::string::String>;
}
#[cfg(test)]
mod tests {
use super::*;
struct DomainRouter;
impl EventRouter for DomainRouter {
fn resolve_topic(&self, event_type: &str) -> crate::HexResult<std::string::String> {
let parts: std::vec::Vec<&str> = event_type.split('.').collect();
if parts.len() < 2 {
return std::result::Result::Err(crate::Hexserror::validation(
"Event type must be in reverse-DNS format",
));
}
let domain = parts[1];
let topic = format!("{}.events", domain);
std::result::Result::Ok(topic)
}
fn resolve_subject(&self, aggregate_id: &str) -> std::option::Option<std::string::String> {
std::option::Option::Some(std::string::String::from(aggregate_id))
}
}
struct EntityRouter;
impl EventRouter for EntityRouter {
fn resolve_topic(&self, event_type: &str) -> crate::HexResult<std::string::String> {
let parts: std::vec::Vec<&str> = event_type.split('.').collect();
if parts.len() < 3 {
return std::result::Result::Err(crate::Hexserror::validation(
"Event type must have entity segment",
));
}
let entity = parts[2];
let topic = format!("{}.events", entity);
std::result::Result::Ok(topic)
}
fn resolve_subject(&self, aggregate_id: &str) -> std::option::Option<std::string::String> {
let subject = format!("aggregate:{}", aggregate_id);
std::option::Option::Some(subject)
}
}
struct FlatRouter;
impl EventRouter for FlatRouter {
fn resolve_topic(&self, _event_type: &str) -> crate::HexResult<std::string::String> {
std::result::Result::Ok(std::string::String::from("all.events"))
}
fn resolve_subject(&self, _aggregate_id: &str) -> std::option::Option<std::string::String> {
std::option::Option::None
}
}
#[test]
fn test_domain_router_resolves_topic() {
let router = DomainRouter;
let topic = router.resolve_topic("com.example.user.created").unwrap();
std::assert_eq!(topic, "example.events");
}
#[test]
fn test_domain_router_resolves_subject() {
let router = DomainRouter;
let subject = router.resolve_subject("user-123");
std::assert_eq!(
subject,
std::option::Option::Some(std::string::String::from("user-123"))
);
}
#[test]
fn test_domain_router_rejects_invalid_format() {
let router = DomainRouter;
let result = router.resolve_topic("invalid");
std::assert!(result.is_err());
}
#[test]
fn test_entity_router_resolves_topic() {
let router = EntityRouter;
let topic = router.resolve_topic("com.example.user.created").unwrap();
std::assert_eq!(topic, "user.events");
}
#[test]
fn test_entity_router_resolves_subject_with_prefix() {
let router = EntityRouter;
let subject = router.resolve_subject("user-123");
std::assert_eq!(
subject,
std::option::Option::Some(std::string::String::from("aggregate:user-123"))
);
}
#[test]
fn test_entity_router_rejects_invalid_format() {
let router = EntityRouter;
let result = router.resolve_topic("com.example");
std::assert!(result.is_err());
}
#[test]
fn test_flat_router_uses_single_topic() {
let router = FlatRouter;
let topic1 = router.resolve_topic("com.example.user.created").unwrap();
let topic2 = router.resolve_topic("com.example.order.shipped").unwrap();
std::assert_eq!(topic1, "all.events");
std::assert_eq!(topic2, "all.events");
}
#[test]
fn test_flat_router_returns_no_subject() {
let router = FlatRouter;
let subject = router.resolve_subject("user-123");
std::assert_eq!(subject, std::option::Option::None);
}
#[test]
fn test_multiple_event_types_routing() {
let router = DomainRouter;
let topic1 = router.resolve_topic("com.acme.user.created").unwrap();
let topic2 = router.resolve_topic("com.widgets.order.shipped").unwrap();
std::assert_eq!(topic1, "acme.events");
std::assert_eq!(topic2, "widgets.events");
}
}