Signal Router
View SourceHigh-performance trie-based signal routing with pattern matching and priority execution.
Route Patterns
Exact Matches
{"user.created", HandleUserCreated}
{"payment.processed", ProcessPayment}Single Wildcards (*)
Matches exactly one segment:
{"user.*.updated", HandleUpdate} # matches "user.profile.updated", not "user.profile.settings.updated"
{"*.error", HandleError} # matches "auth.error", "db.error"Multi-level Wildcards (**)
Matches zero or more segments:
{"audit.**", AuditLogger} # matches "audit", "audit.user", "audit.user.login"
{"metrics.**", MetricsCollector} # matches all metrics signalsPattern Rules
- Segments:
[a-zA-Z0-9._*-]+ - No consecutive dots (
..) - No consecutive multi-wildcards (
**...**)
Trie-Based Matching
The router organizes handlers in a prefix tree for O(log n) lookup performance:
# Routes stored as trie nodes
user
├── created (handler: HandleUserCreated)
├── * (handler: HandleUserWildcard)
└── profile
└── updated (handler: HandleProfileUpdate)Matching algorithm:
- Exact segment match
- Single wildcard (
*) match - Multi-level wildcard (
**) match
Route Priorities
Handlers execute by priority order (-100 to 100):
# High priority audit logging
{"audit.**", AuditLogger, 100}
# Default priority business logic
{"user.created", HandleUserCreated, 0}
# Low priority metrics
{"**", MetricsCollector, -75}Complexity Scoring
Priority calculation:
- Base score:
segment_count * 2000 - Exact match bonus:
3000 * (segment_count - position) - Wildcard penalties:
- Single (
*):1000 - position * 100 - Multi (
**):2000 - position * 200
- Single (
More specific paths execute before wildcards.
Dynamic Routing
Adding Routes
{:ok, router} = Router.add(router, [
{"metrics.**.latency", LatencyTracker, 50},
{"system.error", ErrorHandler}
])Removing Routes
{:ok, router} = Router.remove(router, ["metrics.**", "old.route"])Pattern Matching Functions
large_payment_filter = fn signal ->
signal.data.amount > 1000
end
{:ok, router} = Router.add(router, [
{"payment.processed", large_payment_filter, AlertLargePayment, 75}
])Multiple Dispatch
{"system.error", [
{MetricsAdapter, [type: :error]},
{AlertAdapter, [priority: :high]},
{LogAdapter, [level: :error]}
]}Signal Routing
{:ok, targets} = Router.route(router, %Signal{
type: "user.profile.updated",
data: %{user_id: "123"}
})
# Returns: [HandleUserWildcard, HandleProfileUpdate] (priority order)Pattern matching validation:
Router.matches?("user.created", "user.*") # true
Router.matches?("audit.user.login", "audit.**") # true
Router.matches?("user.profile.updated", "user.*") # falseNext Steps
- Signal Extensions - Add custom metadata to signals with CloudEvents compliance
- Signal Journal - Durable storage with causality tracking and conversation management