Versioning messages
Even though I can honestly say that I have developed interfaces that could accommodate any change made to both sides without ever modifying the interface, most people don't design to that extreme. There will, more likely than not, come a time where you will have to change a message to accommodate a new feature or request, and so on. Now, we get into the issue of message versioning.
To enable support for versioned messages, we need to ensure the required components are configured. The simplest way to achieve this is as follows:
var bus = RabbitHutch.CreateBus( "host=localhost", services => services.EnableMessageVersioning() )
Once support for versioned messages is enabled, we must explicitly opt-in any messages we want to be treated as versioned. So as an example, let's say we have a message defined called MyMessage
. As you can see in the following message, it is not versioned and all versions will be treated the same way as any other when it is published:
public class MyMessage { public string Text { get; set; } }
The next message that you see will be versioned, and ultimately it will find its way to both the V2
and previous subscribers by using the ISupersede
interface:
public class MyMessageV2 : MyMessage, ISupersede<MyMessage> { public int Number { get; set; } }
How does message versioning work?
Let's stop for a second and think about what's happening here. When we publish a message, EasyNetQ usually creates an exchange for the message type and publishes the message to that exchange:

Subscribers create queues that are bound to the exchange and therefore receive any messages published to it:

With message versioning enabled, EasyNetQ will create an exchange for each message type in the version hierarchy and bind those exchanges together. When you publish the MyMessageV2
message, it will be sent to the MyMessageV2
exchange, which will automatically forward it to the MyMessage
exchange.
When messages are serialized, EasyNetQ stores the message type name in the type
property of the message properties. This metadata is sent along with your message to any subscribers, who can then use it to deserialize the message.
With message versioning enabled, EasyNetQ will also store all the superseded message types in a header in the message properties. Subscribers will use this to find the first available type that the message can be deserialized into, meaning that even if an endpoint does not have the latest version of a message, so long as it has a version, it can be deserialized and handled.
Message versioning guidance
Here are a few tips for message versioning:
- If the change cannot be implemented by extending the original message type, then it is not a new version of the message; it is a new message type
- If you are unsure, prefer to create a new message type rather than version an existing message
- Versioned messages should not be used with request/response as the message types are part of the request/response contract and
Request<V1,Response>
is not the same asRequest<V2,Response>
, even ifV2
extendsV1
(that is, public classV2 : V1 {}
) - Versioned messages should not be used with send/receive as this is targeted sending and therefore there is a declared dependency between the sender and the receiver