Topology

Messaging topology is a specific arrangement of the messaging entities, such as queues, topics, subscriptions, and rules.

Azure Service Bus transport operates on a topology created on the broker. The topology handles exchanging messages between endpoints, by creating and configuring Azure Service Bus entities.

The built-in topology, also known as ForwardingTopology, was introduced to take advantage of the broker nature of the Azure Service Bus and to leverage its native capabilities.

The topology creates a single input queue per endpoint and implements a publish-subscribe mechanism, with all publishers using a single topic.

Subscriptions are created under the topic, with one subscription entity per subscribing endpoint. Each subscription contains multiple rules; there's one rule per event type that the subscribing endpoint is interested in. This enables a complete decoupling between publishers and subscribers. All messages received by subscription are forwarded to the input queue of the subscriber.

ForwardingTopology

Quotas and limitations

The ForwardingTopology supports up to 2,000 endpoints with up to 2,000 events in total. Since multiple rules per subscription are used, topics cannot be partitioned.

Subscription rule matching

[NServiceBus.EnclosedMessageTypes] LIKE '%(full class name)%'

The SQL rules added on the subscriber side will match the EnclosedMessageTypes header to the fully qualified class name, including % at the beginning and % at the end. In this case, % follows standard SQL syntax and stands for any string of zero or more characters.

For example, a subscriber interested in the event Shipping.OrderAccepted will add the following rule to the subscription:

[NServiceBus.EnclosedMessageTypes] LIKE '%Shipping.OrderAccepted%'

The subscription rules uses % both at the beginning and at the end of the LIKE clause to support the following scenarios:

  • Interface-based inheritance
  • Evolution of the message contract
Interface-based inheritance

A published message type might have in its hierarchy multiple valid interfaces that represent a message type. For example:

namespace Shipping;

interface IOrderAccepted : IEvent { }
interface IOrderStatusChanged : IEvent { }

class OrderAccepted : IOrderAccepted, IOrderStatusChanged { }
class OrderDeclined : IOrderAccepted, IOrderStatusChanged { }

In this scenario, the EnclosedMessageTypes headers looks like the following when publishing the OrderAccepted event:

Shipping.OrderAccepted, Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XYZ;Shipping.IOrderAccepted, Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XYZ;Shipping.IOrderStatusChanged, Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XYZ

For a handler class OrderAcceptedHandler : IHandleMessages<OrderAccepted> the subscription will look like:

[NServiceBus.EnclosedMessageTypes] LIKE '%Shipping.OrderAccepted%'

This ensures that the condition matches the EnclosedMessageTypes header shown previously.

If the subscriber is interested only in the interface IOrderStatusChanged, it will declare a handler class OrderStatusChanged : IHandleMessages<IOrderStatusChanged> and the corresponding subscription will look like:

[NServiceBus.EnclosedMessageTypes] LIKE '%Shipping.IOrderStatusChanged%'

This matches any EnclosedMessageTypes header that contains Shipping.IOrderStatusChanged regardless of the position in the header. When a publisher starts publishing Shipping.OrderDeclined the event will automatically be routed into the subscriber's input queue without any topology changes.

Evolution of the message contract

As mentioned in versioning of shared contracts and also shown in the examples above, NServiceBus uses the fully-qualified assembly name in the message header. Evolving the message contract encourages creating entirely new contract types and then adding a version number to the original name. For example, when evolving Shipping.OrderAccepted a new contract would be created by the publisher called Shipping.OrderAcceptedV2. When the publisher publishes Shipping.OrderAcceptedV2 events, the enclosed message type would look like the following:

Shipping.OrderAcceptedV2, Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XYZ;Shipping.IOrderAccepted, Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XYZ;Shipping.IOrderStatusChanged, Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XYZ

Any existing subscriber that subscribes to:

[NServiceBus.EnclosedMessageTypes] LIKE '%Shipping.OrderAccepted%'

will still receive those events automatically since the base name of Shipping.OrderAcceptedV2 is Shipping.OrderAccepted. New subscribers subscribing to the updated contract can subscribe with:

[NServiceBus.EnclosedMessageTypes] LIKE '%Shipping.OrderAcceptedV2%'

which matches the newly created contract more closely.

Topology highlights

Decoupled Publishers / Subscribersyes
Polymorphic events supportyes
Event overflow protectionyes
Subscriber auto-scaling based on queue sizeyes
Reduced number of connections to the brokeryes

Last modified