Direct & Topic Messages and Auto-Binding Guide¶
This page explains how to use direct messages (RabbitMQ direct exchanges), topic messages (RabbitMQ topic exchanges), and the auto-binding behavior added to consumer definitions.
What problem this solves¶
Direct/topic exchanges require routing keys. Manually configuring:
PublishDirect<T>()/PublishTopic<T>()in every service that publishes/consumes, and- endpoint bindings (
Bind<T>(routingKey)/BindTopic<T>(pattern))
is easy to forget and easy to misconfigure.
Message attributes¶
[Direct]¶
Marks a message as being published to a direct exchange.
| C# | |
|---|---|
[Topic]¶
Marks a message as being published to a topic exchange.
| C# | |
|---|---|
[RoutingKey] (optional)¶
Marks a single property/field used to compute the routing key automatically when publishing.
- 0 members marked: OK (routing key may be provided manually at publish-time).
- 1 member marked: OK.
- 2+ members marked: ❌ configuration should fail (ambiguous).
| C# | |
|---|---|
Publishing messages¶
Direct publishing¶
| C# | |
|---|---|
Case A: message has [RoutingKey]
If the message has exactly one [RoutingKey] member, the module auto-configures the formatter and you can just publish:
| C# | |
|---|---|
Case B: message has NO [RoutingKey] (manual routing key)
If the message is [Direct] but has no [RoutingKey], the module configures only PublishDirect<T>(). Provide a key manually when needed:
| C# | |
|---|---|
Important
If you publish to a direct exchange without a routing key (or with the wrong key), consumers bound to different routing keys will never receive the message.
Topic publishing¶
| C# | |
|---|---|
Topic routing keys are dot-separated words that can be matched by consumer patterns (see below).
If the message has no [RoutingKey], you typically publish with a manual routing key:
| C# | |
|---|---|
Consuming messages (auto-binding)¶
The framework exposes a simple binding API for any consumer whose definition implements
AutoBindConsumerDefinition (or any of its derivatives, including ResilientUnitOfWorkConsumerDefinition).
Direct bindings¶
Override:
| C# | |
|---|---|
Bindings use:
endpointConfigurator.Bind<T>()endpointConfigurator.Bind<T>(routingKey)
Example: many messages, same routing key
Topic bindings¶
Override:
| C# | |
|---|---|
Bindings use:
endpointConfigurator.BindTopic<T>(pattern)endpointConfigurator.BindTopic(exchangeName, pattern)
Topic patterns support RabbitMQ wildcards:
*matches exactly one word#matches zero or more words
Example: bind a topic message with patterns
Important
Many routing maps store a single resolver per message type.
If you call map.Bind
How auto-binding behaves¶
When a consumer consumes at least one routed message ([Direct] or [Topic]):
- Consume topology is disabled (
ConfigureConsumeTopology = false) only if the auto-binding layer can bind something. - Non-routed consumed messages are bound using
Bind<T>(). - Direct messages are bound using:
Bind<T>(routingKey)when routing keys are configured inConfigureDirectRoutingKeys.- Topic messages are bound using:
BindTopic<T>(pattern)when patterns are configured inConfigureTopicRoutingPatterns.
Backward compatibility¶
Existing consumers that already manually bind exchanges keep working:
- If you don’t override
ConfigureDirectRoutingKeys/ConfigureTopicRoutingPatterns, auto-binding does nothing. - Manual
endpointConfigurator.Bind<...>()/BindTopic<...>()calls remain valid.
This allows incremental migration: update consumers only when it adds value.
Recommended conventions¶
Direct¶
- Prefer
[RoutingKey]on direct messages that always route by a well-known key. - Use manual routing keys only for “advanced” scenarios.
- Keep routing keys stable. Treat them like part of the contract.
Topic¶
- Use dot-separated routing keys like:
domain.entity.event - Example:
landings.request.submitted - Prefer a stable prefix per bounded context (
landings.*,rbac.*, etc.). - Avoid “random strings” as routing keys: debugging becomes painful fast.
Troubleshooting¶
Message published but never consumed¶
Common causes:
- consumer did not bind the routing key/pattern (
Bind<T>(key)/Bind<T>(pattern)missing) - routing key does not match the consumer binding (direct: exact match, topic: wildcard rules)
- message has no
[RoutingKey]and publish code forgot to set the routing key manually
Startup fails due to [RoutingKey]¶
Check:
- message has exactly one
[RoutingKey]member - you didn’t annotate both a property and a field