DEV Community

Cover image for Catching the Bus? How a Service Bus and Azure Functions Can Help Your Integration Reliability
Simon Napper
Simon Napper

Posted on

Catching the Bus? How a Service Bus and Azure Functions Can Help Your Integration Reliability

What’s the first thing that you think of when you hear the word “bus”?

It’s not often how reliable they are, but this article would like to change your perception on the word bus and how it can be a byword for greater reliability, scalability, and stopping integrations from being tightly coupled with each other.

So, what is a Service Bus?

Well it gets its name from the communication system inside a computer that transfer data to and from components (thanks Wikipedia) however it’s really more analogous to a secure mailroom, as it’s a safe store for “message” that pass to and from different computer systems.

Digital Void of Oblivion

Have you ever had a situation where you’ve had to integrate with an API provider that boosts 99% uptime, only to find that you seem to be in that unwanted 1% every time you use it? When that happens your call to the API and the data therein could be lost forever, cast into the digital void of oblivion, which always happens when your client is using the system.

Chasm of Doom

This is where the Service Bus comes in. It’s aim is to help you bridge that gap over the chasm of doom, by sending your data to the Service Bus as a “message” (yes this terminology does get confusing when you’re integrating with email systems) which is then held by the Service Bus, until it can successfully deliver it to the API that you’re integrating with.

Silicon Heaven

If that API is down, then the Service Bus will keep on trying for a certain number of attempts at a certain time interval, until it is successful. If it can never be delivered, then the data is moved out of the way into what’s called a “Dead Letter”, waiting to be looked at and either resent or deleted, but most importantly, it still exists with the data intact and hasn’t just vanished to silicon heaven.

Utopia

So now we are approaching a Utopia, a system that never loses its data, keeps trying automatically to resend that data and will store it for you if it can’t deliver it, perfection. Well maybe not perfection, but we are adding resilience into our architecture and ensuring that our communications between systems like Umbraco and third party API’s now have a higher chance of success or at the very least, we’re not losing our data to the vast, digital great beyond.

Service Bus Code Example

Here’s an example of how you can set up a Service Bus helper and hook that into firing when content is published in Umbraco. This use the Azure.Messaging.ServiceBus nuget package.

Create a Helper for Interacting with the Service Bus

public class ServiceBusHelper
{
    private readonly string _connectionString;
    private readonly string _topicName;

    public ServiceBusHelper(string connectionString, string topicName)
    {
        _connectionString = connectionString;
        _topicName = topicName;
    }

    public async Task SendMessageAsync(string message)
    {
        await using var client = new ServiceBusClient(_connectionString);
        ServiceBusSender sender = client.CreateSender(_topicName);

        ServiceBusMessage busMessage = new ServiceBusMessage(message);
        await sender.SendMessageAsync(busMessage);
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, inject this into your ContentPublishedNotification

public class ContentPublishHandler : INotificationHandler<ContentPublishedNotification>
{
    private readonly ServiceBusHelper _serviceBusHelper;
    private readonly ILogger<ContentPublishHandler> _logger;

    public ContentPublishHandler(ServiceBusHelper serviceBusHelper, ILogger<ContentPublishHandler> logger)
    {
        _serviceBusHelper = serviceBusHelper;
        _logger = logger;
    }

    public async void Handle(ContentPublishedNotification notification)
    {
        foreach (var content in notification.PublishedEntities)
        {
            await _serviceBusHelper.SendMessageAsync($"Content published: {content.Name}");
            _logger.LogInformation($"Sent message for content: {content.Name}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Don’t forget to register it!

builder.Services.AddSingleton(new ServiceBusHelper("your-connection-string", "your-topic-name"));
 builder.AddNotificationHandler<ContentPublishedNotification, ContentPublishHandler>();
Enter fullscreen mode Exit fullscreen mode

The Betrayal

So now we have our Umbraco solution sending messages to the Service Bus and the API is receiving data and you’re happy, I’m happy, the client is happy, job well done and time to put your feet up.

Well… not quite as I may have mislead you a smidgen.

As you might have noticed, the title of this has the words “Azure Functions” in it and as of yet, I’ve not mentioned a thing about them, however they are a very important part of this proposed set up, in fact this won’t work without them.

Groovy Baby

So what are and where do Azure Functions feature in all this.

Well according to the inter-web, Azure Functions is a serverless compute service provided by Microsoft Azure that allows you to run small pieces of code, or "functions," in the cloud without managing the underlying infrastructure. It’s designed to let developers focus on their code and application logic without worrying about server provisioning, scaling, or maintenance.

Well that sounds groovy, but what does it mean practically?

“Alright, Dave?”

Well for the purposes of this example, Azure Functions are bits of code that will be triggered (ok it's a bad Only Fools and Horses joke) by the arrival of a Service Bus message and will then act on that message accordingly and the data that the message contains (so could be form data, etc).

For this example of the Service Bus, were going to use a Topic in order to differentiate between the different messages that we’re sending.

When we send a Service Bus message, we specify the Topic that it belongs to, so that it ends up in the right place. We can then set the Function to poll for that Topic and to then process it when it arrives.

That action could be sending an email, sending data to an API, doing both and logging stuff, whatever you want but importantly, it will run that code and return a success or failure on the action you’re performing.

The Service Bus will then either mark message as successful and remove it or if there’s an exception, keep trying until it either gets a successful return or it hits the limit of the amount of tries it should do. So if you’re trying to send something to a busy or dare I say it, unreliable API, the combination of the Service Bus and Azure Functions will keep trying to send your data to the API until it goes through (or doesn’t).

Code Example for Azure Function

The following code takes in a message sent to the correct Topic in the Service Bus and then attempts to send an email with that message. This code has to be in an Azure Functions project in Visual Studio to work:

public class ServiceBusEmailFunction
{
    private readonly ILogger<ServiceBusEmailFunction> _logger;

    public ServiceBusEmailFunction(ILogger<ServiceBusEmailFunction> logger)
    {
        _logger = logger;
    }

    [Function("ServiceBusEmailFunction")]
    public async Task RunAsync(
        [ServiceBusTrigger("your-queue-or-topic-name", Connection = "ServiceBusConnection")] string message)
    {
        _logger.LogInformation($"Received Service Bus message: {message}");

        // Define email details
        string recipient = "recipient@example.com"; 
        string subject = "Service Bus Notification";
        string body = $"Message received from Service Bus: {message}";

        try
        {
            using (var smtpClient = new SmtpClient("smtp.yourserver.com", 587))
            {
                smtpClient.Credentials = new NetworkCredential("your-email@example.com", "your-password");
                smtpClient.EnableSsl = true; // Enable SSL if required

                var mailMessage = new MailMessage("your-email@example.com", recipient, subject, body)
                {
                    IsBodyHtml = false
                };

                await smtpClient.SendMailAsync(mailMessage);

                _logger.LogInformation("Email sent successfully.");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError($"Failed to send email: {ex.Message}");
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

“Wibbly wobbly, timey wimey”

What else can Azure Functions do?

Well you can also have timers which will run at a set time or interval and execute your code. Perfect for daily reports and the such like or replacing Scheduled Tasks that you might still have knocking about on a server (I’m not judging, but it’s not 1999 anymore). You could have the timer Function create a Service Bus message which is then sent to a trigger Function, perhaps one that deals with sending emails or it could simply be standalone and do its thing.

Subscription or Queue?

There is the concept in Azure Service Bus of a Queue or a Subscription/Topic setup. We use the latter for more flexibility. However, for completeness, the difference between them is set out below. For the purposes of this article, we’ll be sticking with the Subscription/Topic setup.

1. Queue (Point-to-Point Messaging)

  • A queue is designed for one-to-one messaging, also known as point-to-point messaging.
  • Messages sent to a queue are delivered to a single receiver, which then processes each message.
  • Multiple receivers can connect to a queue, but only one will process each message, providing a load-balancing effect.
  • Queues are best suited for cases where there is a single consumer for each message, such as when an order-processing service handles incoming orders one at a time.

2. Topic/Subscription (Publish-Subscribe Messaging)

  • A topic is used in publish-subscribe scenarios, where a single message can be delivered to multiple subscribers.
  • Each topic can have multiple subscriptions, which are like "virtual queues" that receive copies of the messages sent to the topic.
  • When a message is sent to a topic, it can be received by multiple subscribers based on subscription rules and filters, allowing each subscription to define specific criteria for the messages it wants to receive.
  • Topics and subscriptions are ideal for one-to-many scenarios, such as when notifications need to be broadcasted to multiple services or applications (e.g., sending alerts to multiple monitoring services).

In Summary:

  • Use queues when you have a single consumer or want messages distributed evenly to multiple competing consumers.
  • Use topics/subscriptions when you want a message to be sent to multiple recipients, each of which may have unique filtering criteria for the messages they receive.

The Ol’ Switcheroo

A good advantage of using this architectural approach is that it simplifies your interactions in a DXP composable solution.

As long as your Service Bus message is reused, you can create a new Function to switch from say Mandrill to SendGrid and just disable one, enable the other and Bob’s your uncle.

“Your scientists were so preoccupied with whether they could, they didn’t stop to think if they should”

So there are a couple of considerations to keep in mind when using this approach.

Besides the financial cost, which will vary depending on the size of your integration, it's another thing to manage as part of your solution. This then increases the skill set you need in a developer to maintain and develop your solution.

Last thing of note is whilst nearly all the Service Bus functionality can be ran via the API, you can’t run all of the functionality for the Service Bus via the Azure Portal. For example, you can’t delete a dead letter message through there, you have to do that via the API, so you might need to code that for maintenance.

And Finally…

In conclusion, adding a combination of a Service Bus and Functions can greatly increase the resilience of your architecture when dealing with third party integrations. In the right solution and with monitoring set up, it’ll ensure that you have much more reliable and successful communication and is perfect for composable DXP architecture.

All aboard the Service Bus, ding ding!

Top comments (0)