In this post I revisit the 3 most fundamental messaging patterns used when designing and implementing loosely coupled, distributed applications. I will also give a brief overview how these three patterns are implemented when using NServiceBus as a high level Messaging abstraction over a Service Bus such as Azure ServiceBus or RabbitMQ.
In our company we created and operate a highly distributed, cloud based application. Most of the communication between individual parts of the application happens over a ServiceBus. In our case this is Azure ServiceBus in the cloud and RabbitMQ on individual developer’s laptops during development. 100% of our software runs in containers.
Our teams have been growing rapidly over the last year and as a consequence we not every new developer or DevOps engineer is equally familiar with the concepts of messaging. This is the main reason for this blog post. In this post I want to revisit the basics of messaging in a distributed application.
Let’s define our system that we use to explain the concepts first.
We have two components A and B. They may or may not be deployed on the same physical server or VM. It doesn’t really matter. Important is, that components A and B run in different processes and do not have a public API – such as a restful API – to communicate over. The only means of communication in this scenario is through messages. The transport of the messages between A and B happens via what we call a “bus”.
Now that we have defined our sample system let’s define what kind of pattern we can distinguish when observing the messaging between components A and B. There exist 3 main patterns of messaging and we will look at each of them individually. Let’s fist name them:
- Command pattern
- Publish and subscribe
Next I will introduce the command pattern.
The command pattern clearly defines one of the components – say component A – as the originator of the command. In this case component B is the target of the command. In this pattern there can always only be one single target of a command, not two and not zero. The originator knows to whom to send the command.
The originator or sender of the command does not expect an (immediate) answer. If at all the sender expects an answer then it may be in the future through another means. Basically from the perspective of the originator, it is a “send and forget” type of operation.
Please note, that the command is sent over the bus. For simplicity I have omitted the bus in the above picture though.
Typical examples taken from real business applications implementing the above scenario could encompass commands such as:
- Checkout a shopping cart
- Submit or ship an order
- Cancel an appointment
- Deliver a parcel
Next let’s look at the request-response pattern.
Request – Response Pattern
This pattern is very similar to the Command Pattern with the difference that the originator sends some command and expects an (immediate) response. In this case we often call the command a request instead. The originator sends a request to the target and the target sends a response back to the originator.
In this scenario the originator, after sending the request, waits for a message coming back from the target containing the response to the request.
This pattern must be very familiar to you if you have done any web development where the client of your application sends an HTTP GET to a backend API which in turn answers this request with a HTTP response object.
In messaging though we are rather discouraged to use this pattern since it somewhat contradicts the core idea of loosely coupled component communicating with each other through messages.
Let’s not look at the most familiar messaging pattern, the publish and subscribe pattern.
Publish and Subscribe Pattern
The third and last of the patterns we want to discuss in this post is the publish and subscribe pattern. In messaging it is probably the most common pattern. In this scenario an originator wants to notify the “outside world” – that is, other interested parties – that something noteworthy has happened. We often use the term “the originator publishes an event” for this. The originator does not and should not know, who will react to its notification or event. Any external party who wants to react to a given notification or event is said to subscribe to it.
This is very important to notice; here the originator does not know how many, or if any listeners are actually reacting. This is an important distinction to the command pattern where the originator exactly knows to whom it sends the command. Also in the latter pattern the command can only be sent to exactly one target.
In the above picture we have the originator component A which publishes an event and we have component B and C that subscribe to that event. That is, component B and C are interested in that particular event and want to react to it. On the other side component D is not interested and thus does not subscribe to the event.
Now that we have a good understanding of what the 3 most common messaging patterns are, let’s look at some code samples.
Messaging Samples using NServiceBus
In our company we are using NServiceBus (NSB) as a high level abstraction over the low level messaging infrastructure which is Azure ServiceBus (Azure SB) in the cloud and RabbitMQ on a local laptop of each developer.
Let’s once again start with the command pattern and how it is implemented when using NServiceBus.
NSB and the Command Pattern
The first code snippet shows how to send a command from the originator and how to handle it on the target:
As you can see, the command
CheckoutShoppingCart is just a plain old C# object (POCO); nothing fancy there. Note that for discoverability by the NSB the command object has to implement the
IMessage marker interface. The code in the originator just uses the
Send method – which is asynchronous – but does not expect any answer. Finally on the target side we have to implement a so called command handler. In our case this is the
CheckoutShoppingCartHandler class. The command pattern and how to use it with NSB is described in more detail here.
Next we give a brief introduction in how to implement the request-response pattern with NServiceBus.
NSB and the Request-Response Pattern
The next code snippet shows how one can implement the request-response pattern with NSB. Since this pattern is only recommended for special situations the necessary infrastructure is not available in NSB’s core NuGet package but one has to add an extra NuGet package called
As you can see in the above snippet, we first define the request and response objects both as POCOs. Note that for discoverability by the NSB the message objects have to implement the
IMessage marker interface. Then we have to specifically configure the originator endpoint for supporting callbacks.
Executing the request is done through the
Request method of the endpoint context declaring the type of the response as a generic argument. The handler in the target endpoint looks very similar to a command handler with the sole exception that at the end of its code one executes the
Reply method on the context to send back the response to the originator.
Callbacks and how to use them with NSB is described in more details here.
Let’s now look at the publish and subscribe pattern and how it is implemented when using NSB.
NSB and the Publish & Subscribe Pattern
Finally let’s look how we can implement the publish & subscribe pattern with NSB. Below is a summary of the important code snippets.
Also in this case the events are POCOs and for discoverability by NSB they should implement the marker interface
IEvent. Please note that usually an event is triggered by a handler that handles a preceding command message, but there can be other scenarios as well. Also note that in our case the
OrderSubmittedHandler is triggering the shipping of the product and when done also publishes an event. This time the
Once again, more details about the publish & subscribe pattern and how to implement it when using NSB can be found here.
In this post I have given a brief introduction to the three most fundamental patterns used in messaging in a loosely coupled and distributed application. These are the command, the request-response and the publish & subscribe patterns. I also pointed out that specifically the request response pattern is to handle with special care and only use in very specific situations where there is no better alternative since this pattern is somewhat contradicting the idea of implementing a loosely coupled application architecture. Finally I gave a brief overview on how one would implement each pattern when using NServiceBus, which is a high level Messaging Infrastructure that can be used in conjunction with low level messaging infrastructure such as Azure ServiceBus or RabbitMQ.