Expand description
Content-Based Router pattern: route an Exchange to one of several processors based on a header value.
This implements the classic Enterprise Integration Pattern (EIP) “Content-Based Router”. It examines a specific message header and selects a processor whose configured value matches.
§Why It Exists
In message-driven integrations you often need to send different message types (or business events)
through distinct processing flows. Instead of embedding conditional logic inside processors or
scattering if/else routes, a dedicated router centralizes this decision, improving readability
and testability.
§Why In patterns Folder
The patterns module groups canonical EIP building blocks (filter, splitter, aggregator, etc.).
ContentBasedRouter is one of those core patterns, so it lives alongside them for discoverability
and conceptual cohesion rather than in a generic utilities namespace.
§Behavior
- Looks up a header (configured via
new(header_name)). - If the header matches a registered route value (added via
when(value, processor)), the associated processor is invoked. - If no route matches, returns a
Routingerror (Error::Routing). - Exactly one processor is invoked (first matching key lookup).
§Async vs Sync
The router itself is lightweight; it defers to the selected processor. Under the async feature
its process method is async and awaits the downstream processor; without it, synchronous dispatch occurs.
§Example (basic usage)
use allora_core::{patterns::content_router::ContentBasedRouter, processor::ClosureProcessor, route::Route, Exchange, Message};
let hi = ClosureProcessor::new(|exchange| { exchange.out_msg = Some(Message::from_text("hi route")); Ok(()) });
let bye = ClosureProcessor::new(|exchange| { exchange.out_msg = Some(Message::from_text("bye route")); Ok(()) });
let router = ContentBasedRouter::new("kind").when("hi", Box::new(hi)).when("bye", Box::new(bye));
let route = Route::new().add(router).build();
let mut exchange = Exchange::new(Message::from_text("payload"));
exchange.in_msg.set_header("kind", "hi");
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async { route.run(&mut exchange).await.unwrap(); });
assert_eq!(exchange.out_msg.unwrap().body_text(), Some("hi route"));§Error Handling
No match: Error::Routing("no matching route").
Downstream processor errors propagate unchanged.
§Testing Strategies
- Provide several
whenroutes, assert correct one chosen. - Assert error when header missing.
- Use processors that mutate headers to verify only one is executed.
§Future Extensions
- Predicate-based routing (closures) instead of plain equality.
- Default / fallback processor.
- Support for wildcards / pattern matching.
- Metrics counters (per route hit counts).