ASP.NET Core and Azure Service Bus: Message Queues, Topics, and Subscriptions
A practical guide to building reliable messaging systems with queues, topics, and subscriptions in ASP.NET Core
Modern applications rarely operate as a single, tightly connected system. Instead, they rely on messaging to communicate between services reliably and at scale. In this guide, we’ll explore how to use Azure Service Bus with ASP.NET Core to implement queues, topics, and subscriptions that keep your applications responsive, resilient, and ready for growth.
As applications grow, direct communication between components becomes harder to manage. One service calling another synchronously might work early on, but it quickly introduces problems. If one service is slow or unavailable, everything depending on it starts to fail. That’s where messaging comes in.
Messaging allows systems to communicate without being directly connected. Instead of waiting for a response, one part of your application sends a message, and another part processes it when it’s ready. This decoupling is one of the key building blocks of scalable systems.
Azure Service Bus is Microsoft’s fully managed message broker designed for enterprise-grade messaging. It provides reliable message delivery, supports complex communication patterns, and integrates naturally with ASP.NET Core applications.
In this article, we’ll walk through how Azure Service Bus works, how to integrate it into your ASP.NET Core applications, and how to design messaging patterns that scale in real-world systems.
Why Messaging Matters in Modern Applications
In traditional applications, components often communicate directly using HTTP. While simple, this approach creates tight coupling. If one service is unavailable, the entire workflow can break.
Messaging solves this problem by introducing an intermediary layer.
Instead of Service A calling Service B directly, Service A sends a message to a queue. Service B processes that message independently. This means Service A doesn’t need to wait, retry, or even know whether Service B is currently online.
This approach improves:
Reliability, because messages are stored until processed
Scalability, because processing can happen in parallel
Resilience, because failures don’t cascade across services
Azure Service Bus is built specifically to handle these scenarios at scale.
According to Microsoft’s official documentation, it supports reliable message delivery, ordering, transactions, and advanced routing patterns
https://learn.microsoft.com/azure/service-bus-messaging/service-bus-messaging-overview
Understanding Azure Service Bus Concepts
Before diving into implementation, it’s important to understand the core building blocks.
Queues
Queues follow a simple pattern. A sender places a message into a queue, and a receiver processes it.
Each message is processed by a single consumer. This is useful for background jobs, task processing, and workloads that should only run once.
Think of queues as a to-do list. Each task is picked up and completed by one worker.
Topics and Subscriptions
Topics extend the queue model by allowing multiple consumers to receive the same message.
Instead of one receiver, messages published to a topic can be delivered to multiple subscriptions. Each subscription acts like its own queue.
This pattern is called publish-subscribe.
For example, when an order is placed:
One service processes payment
Another updates inventory
Another sends notifications
All of them receive the same message independently.
Dead-Letter Queues
Sometimes messages fail to process. Instead of losing them, Azure Service Bus moves them to a dead-letter queue.
This allows developers to inspect failed messages, debug issues, and retry processing.
Setting Up Azure Service Bus
To get started, you’ll need an Azure Service Bus namespace.
You can create one in the Azure Portal and then create:
A queue for simple messaging
A topic for publish-subscribe scenarios
Once created, you’ll get a connection string, which your ASP.NET Core application will use.
Install the official SDK:
dotnet add package Azure.Messaging.ServiceBusMicrosoft’s SDK documentation provides details on installation and usage
https://learn.microsoft.com/dotnet/api/overview/azure/messaging.servicebus-readme
Sending Messages from ASP.NET Core
Let’s start with sending messages to a queue.
var client = new ServiceBusClient(connectionString);
var sender = client.CreateSender(”orders-queue”);
var message = new ServiceBusMessage(”New order received”);
await sender.SendMessageAsync(message);This code creates a message and sends it to a queue.
In a real application, the message would likely be serialized JSON representing a business event.
Receiving Messages in ASP.NET Core
Receiving messages typically happens in a background service.
If you’re new to background processing patterns, you may also find our guide on batch processing in ASP.NET Core helpful:
https://www.asptoday.com/p/implementing-bulk-operations-and-batch-processing-in-aspnet-core-applications
public class OrderProcessor : BackgroundService
{
private readonly ServiceBusProcessor _processor;
public OrderProcessor(ServiceBusClient client)
{
_processor = client.CreateProcessor(”orders-queue”);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_processor.ProcessMessageAsync += ProcessMessage;
_processor.ProcessErrorAsync += ErrorHandler;
await _processor.StartProcessingAsync(stoppingToken);
}
private async Task ProcessMessage(ProcessMessageEventArgs args)
{
var body = args.Message.Body.ToString();
// Process message here
await args.CompleteMessageAsync(args.Message);
}
private Task ErrorHandler(ProcessErrorEventArgs args)
{
// Log error
return Task.CompletedTask;
}
}This pattern allows your application to process messages continuously in the background.
Using Topics and Subscriptions
Publishing to a topic is similar to sending to a queue.
var sender = client.CreateSender(”orders-topic”);
await sender.SendMessageAsync(new ServiceBusMessage(”Order placed”));Each subscription receives its own copy of the message.
Subscribers process messages independently, which allows multiple services to react to the same event.
Message Serialization Best Practices
Messages should be structured and versioned carefully.
A common approach is JSON serialization:
var payload = JsonSerializer.Serialize(order);
var message = new ServiceBusMessage(payload);Avoid tightly coupling message formats to internal models. Instead, use contracts that can evolve over time.
Reliability and Message Delivery
Azure Service Bus guarantees at-least-once delivery. This means messages may be delivered more than once.
Your application should handle duplicate messages safely.
This is known as idempotency.
For example, if processing an order, ensure that processing the same message twice does not create duplicate records.
Handling Failures and Retries
Failures are inevitable in distributed systems.
Azure Service Bus includes built-in retry mechanisms. If processing fails, messages can be retried automatically.
If retries exceed a limit, messages move to the dead-letter queue.
You can then inspect and reprocess them.
Scaling Message Processing
One of the biggest advantages of messaging is scalability.
You can run multiple instances of your worker service, and Azure Service Bus will distribute messages across them.
This allows your system to handle increased workloads without changing your core logic.
Integrating with ASP.NET Core Dependency Injection
You should register Service Bus clients using dependency injection.
builder.Services.AddSingleton(_ =>
new ServiceBusClient(connectionString));This ensures efficient reuse of connections and improves performance.
Monitoring and Observability
Monitoring messaging systems is critical.
You should track:
message throughput
processing time
failure rates
dead-letter queue size
Azure Monitor and Application Insights provide visibility into Service Bus operations
https://learn.microsoft.com/azure/azure-monitor
Observability also ties closely with application performance. For a deeper dive into improving system performance, see our guide on performance tuning in ASP.NET Core:
https://www.asptoday.com/p/performance-tuning-aspnet-core-applications
Real-World Example: Order Processing System
Imagine an e-commerce platform.
When a user places an order, the system publishes a message to a topic.
Different services react:
The payment service processes payment
The inventory service updates stock
The notification service sends confirmation emails
Each service operates independently.
If one service fails, others continue working.
This design improves resilience and scalability.
If you're building foundational knowledge, you can also start with our introduction to ASP.NET Core:
https://www.asptoday.com/p/introduction-to-aspnet-core-whats-new-and-why-it-matters
When to Use Queues vs Topics
Queues are best when:
A task should be handled by only one consumer
Topics are best when:
Multiple services need to react to the same event
In most modern systems, both are used together.
Closing Thoughts
Azure Service Bus provides a powerful foundation for building reliable, scalable messaging systems in ASP.NET Core applications.
By using queues, topics, and subscriptions, you can decouple services, improve resilience, and scale your application more effectively.
Messaging isn’t just a technical feature. It’s an architectural shift that allows your system to grow without becoming fragile.
As your applications evolve, adopting messaging patterns early can save significant time and complexity later.
Join The Community
Enjoyed this article? Subscribe to ASP Today for practical insights into ASP.NET Core and modern cloud architecture. Join our Substack Chat to connect with developers building scalable systems and share your experiences.


