Dynamic Message Deserialization
Overview
Brighter supports dynamic type resolution, allowing you to route multiple message types through a single channel. Instead of determining the message type at compile-time through generic parameters, you can use content-based routing where the message type is determined at runtime from metadata.
This enables more flexible messaging patterns while maintaining type safety once the message type is resolved.
DataType Channel Pattern (Default)
By default, Brighter uses the DataType Channel pattern from Enterprise Integration Patterns. In this pattern, each channel carries messages of a single, known type.
DataType Channel Example
// DataType Channel - One type per subscription
var subscription = new KafkaSubscription<TaskCreated>(
new SubscriptionName("paramore.example.tasks"),
channelName: new ChannelName("task.created"),
routingKey: new RoutingKey("task.created"),
groupId: "task-processor",
timeOut: TimeSpan.FromMilliseconds(100)
);Characteristics:
Simple and straightforward
Type-safe at compile-time
One handler per channel
Recommended for most scenarios
Requires separate channel per message type
Cannot handle message type evolution on same channel
When to use DataType Channel:
You have distinct topics/queues for each message type
Message types are stable and don't evolve frequently
You want compile-time type safety
Simple producer-consumer patterns
This is Brighter's default and recommended approach for most scenarios.
Dynamic Message Deserialization
Dynamic message deserialization allows multiple message types on the same channel by resolving the type at runtime based on message metadata.
When to Use Dynamic Deserialization
Dynamic deserialization is useful when:
Multiple related message types share a single topic/queue
Message type evolution - new message types added to existing channels
CloudEvents-based routing - using the CloudEvents
typeattributeContent-based routing - routing decisions based on message content
Shared infrastructure - multiple teams publishing to common topics
How It Works
Instead of specifying the type via a generic parameter, you provide a getRequestType callback in your Subscription that examines the message and returns the appropriate type:
Using CloudEvents Type for Routing
The most common approach for dynamic deserialization is using the CloudEvents type attribute. This provides a standard, interoperable way to identify message types.
CloudEvents Type Routing Example
How it works:
Message arrives on
task.updatechannelBrighter populates
message.Header.Type(CloudEvents type attribute)Callback matches CloudEvents type to Request type
Brighter deserializes message to correct Request type
Routes to appropriate handler based on type
Setting CloudEvents Type on Publication
On the producer side, set the CloudEvents type in your Publication:
All three message types go to the same task.update topic, distinguished by their CloudEvents type.
Custom Routing Strategies
While CloudEvents type is recommended, you can implement any routing strategy by examining message properties.
Routing by Custom Header
Routing by Message Body Content
Note: Parsing the body for routing is less efficient than using headers, but can be useful when integrating with systems that don't support custom headers.
Handler Routing
Once the request type is resolved, Brighter routes the message to the appropriate handler using its standard handler resolution:
Standard 1-to-1 Handler Mapping
With dynamic deserialization:
Message arrives on
task.updatechannelgetRequestTypecallback returnstypeof(TaskCreated)Brighter deserializes to
TaskCreatedRoutes to
TaskCreatedHandler
Integration with Agreement Dispatcher
Dynamic message deserialization can be combined with Agreement Dispatcher for even more flexible routing:
This provides two levels of routing:
Dynamic deserialization: CloudEvents type →
OrderCreatedAgreement dispatcher: Order content → Country-specific handler
Performance Considerations
Dynamic message deserialization has a small performance overhead compared to DataType Channel:
Runtime Type Resolution
DataType Channel (Compile-Time):
Type known at compile-time via generic parameter
Message mapper pipeline pre-built
Minimal runtime overhead
Dynamic Deserialization (Runtime):
Type determined by executing callback function
Message mapper pipeline built on first use per type
Pipeline cached for subsequent messages of same type
Configuration Examples
Kafka with CloudEvents Routing
RabbitMQ with CloudEvents Routing
AWS SQS with CloudEvents Routing
Best Practices
1. Use CloudEvents Type for Routing
CloudEvents provides a standard, interoperable way to identify message types:
2. Provide Comprehensive Type Mappings
Handle all expected message types and provide a clear error for unmapped types:
3. Use Meaningful CloudEvents Types
Follow reverse-DNS naming for CloudEvents types:
See CloudEvents Support for more on CloudEvents type naming.
4. Consider DataType Channel First
Start with DataType Channel (one type per topic) unless you have a specific need for dynamic deserialization:
Only use dynamic when needed:
Multiple related types on same channel
Message evolution scenarios
CloudEvents-based integration
5. Cache Performance-Critical Paths
If performance is critical, pre-warm the pipeline cache:
6. Document Type Mappings
Document which CloudEvents types map to which Request types:
Comparison: DataType Channel vs Dynamic Deserialization
Type Resolution
Compile-time (generic)
Runtime (callback)
Performance
Fastest
Fast
Flexibility
One type per channel
Multiple types per channel
Type Safety
Compile-time
Runtime (after resolution)
Setup Complexity
Simple
Moderate
Message Evolution
Requires new channels
Same channel
CloudEvents Integration
Not needed
Natural fit
When to Use
Default, most scenarios
Multiple types, evolution
Error Handling
Handle unmapped message types gracefully:
Failed messages will go to the dead letter queue based on your failure handling configuration.
Further Reading
Cloud Events Support - Understanding CloudEvents in Brighter
Agreement Dispatcher - Dynamic handler selection
Default Message Mappers - Automatic message mapping
Routing - Message routing in Brighter
Sample Code
Full working examples can be found in the Brighter samples:
Dynamic Deserialization:
Brighter/samples/TaskQueue/- Examples using CloudEvents type routingMulti-type Channels:
Brighter/samples/MultiBus/- Multiple message types on shared infrastructure
Last updated
Was this helpful?
