6.3.2. Implement Azure Service Bus Topics
š” First Principle: The fundamental purpose of a Service Bus Topic is to enable a scalable, one-to-many, publish-subscribe messaging pattern, allowing multiple, independent downstream systems to react to the same event in a decoupled and reliable manner.
Scenario: A central order processing system publishes "Order Placed" events. A separate inventory management service needs to update stock, a notification service needs to send an email, and a logging service needs to archive the event. Each of these services needs to receive a copy of the "Order Placed" message independently.
What It Is: Azure Service Bus Topics provide a publish-subscribe messaging pattern, enabling one-to-many asynchronous communication. When a message is sent to a topic, all active subscriptions receive a copy, allowing multiple services to react independently to the same event.
Key Features:
- Subscriptions: Each subscription acts as a virtual queue associated with a topic, receiving all messages sent to that topic.
- Rules and Filters: Subscriptions can use SQL or correlation filters to receive only messages matching specific criteria, enabling targeted message delivery to specific consumers.
- Message Sessions: Sessions allow grouping of related messages for ordered processing, ensuring that messages with the same session ID are handled sequentially by a single receiver instance.
- Dead-lettering: Like queues, subscriptions have a dead-letter queue for unprocessable messages.
Publishing a Message (C# SDK):
using Azure.Messaging.ServiceBus;
// ... assume ServiceBusClient and ServiceBusSender are initialized for a topic
await sender.SendMessageAsync(new ServiceBusMessage("Hello, Topic!"));
// You can also add properties for filtering
// await sender.SendMessageAsync(new ServiceBusMessage("Order placed!") { Subject = "Order", ApplicationProperties = { { "Region", "EastUS" } } });
Creating a Subscription & Receiving Messages:
- Create subscription (Azure CLI):
az servicebus topic subscription create --resource-group <rg> --namespace-name <ns> --topic-name <topic> --name <sub-name>
- Receive messages (C# SDK):
ServiceBusReceiver receiver = client.CreateReceiver("<topic>", "<subscription>"); ServiceBusReceivedMessage msg = await receiver.ReceiveMessageAsync(); // Receives one message // msg.body, msg.applicationProperties await receiver.CompleteMessageAsync(msg); // Mark message as processed
Common Use Cases:
- Event distribution to multiple microservices that need to react to the same event.
- Application notifications (e.g., sending an event to multiple internal systems when a customer order is placed).
- Fan-out messaging for audit/logging/analytics where multiple systems consume a copy of the same data.
- Workflow orchestration where steps are initiated by a common event.
ā ļø Common Pitfall: Forgetting to create a subscription for a topic. If no subscriptions exist, messages sent to the topic will be dropped and lost.
Key Trade-Offs:
- Filtering Complexity vs. Performance: Complex SQL filters on subscriptions can add a small amount of processing overhead on the broker side compared to simple correlation filters.
Practical Implementation: Subscription with a SQL Filter
# Create a subscription that only receives messages where the 'Region' property is 'EastUS'
az servicebus topic subscription rule create \
--resource-group MyResourceGroup \
--namespace-name MyServiceBusNamespace \
--topic-name OrderEvents \
--subscription-name NotificationServiceSubscription \
--name EastUSOrders \
--filter-sql-expression "Region = 'EastUS'"
Reflection Question: How do Azure Service Bus Topics (with subscriptions and optional rules/filters) fundamentally enable one-to-many asynchronous communication, allowing multiple services to react independently to the same event, supporting scalable and decoupled event-driven architectures?