| 1 | Overview | 2 | Architecture Overview |
| 3 | Azure Service Topology | 4 | Implementation Guide |
| 5 | Decision Criteria | 6 | Cost Model |
| 7 | Anti-Patterns to Avoid | 8 | References |
Overview
In an event-driven architecture, services communicate by publishing events to a bus or queue rather than calling each other directly. The producing service does not know who consumes its events, and consumers do not know who produces them. This inversion of control eliminates the tight coupling that makes monolithic and synchronous microservice systems fragile at scale.
Azure implements this pattern using three purpose-built services: Event Grid for reactive pub/sub routing of discrete domain events, Service Bus for enterprise-grade ordered command delivery where guaranteed processing matters, and Event Hubs for high-throughput telemetry and log streaming at Kafka-protocol scale. Choosing the right service for each flow — and combining them — is the primary architectural decision.
Architecture Overview
The conceptual pattern separates the architecture into three zones: producers that emit events, the event routing layer that filters and routes based on content, and consumers that process events independently and idempotently.
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD START([Business Event Occurs]) subgraph PRODUCERS["Event Producers"] P1[Order Service] P2[Payment Service] P3[Telemetry Agent] end subgraph ROUTING["Event Routing Layer"] BUS[Messaging Tier\nContent-based routing\nSchema validation] RULE1{Domain Events} RULE2{Command Messages} RULE3{Streaming Data} end subgraph CONSUMERS["Event Consumers — Independent and Idempotent"] C1[Notification Service\nCustomer alerts] C2[Analytics Service\nBusiness intelligence] C3[Audit Service\nCompliance logging] C4[Fulfillment Service\nWarehouse trigger] end DLQ[Dead Letter Store\nFailed event storage\nReplay capability] END([System State Updated — No Direct Coupling]) START --> P1 & P2 & P3 P1 & P2 & P3 -->|Publish event| BUS BUS --> RULE1 & RULE2 & RULE3 RULE1 -->|Route| C1 & C2 RULE2 -->|Route| C3 & C4 RULE3 -->|Route| C2 & C3 C1 & C2 & C3 & C4 -->|On failure| DLQ C1 & C2 & C3 & C4 --> END style START fill:#4f8ef7,color:#fff style END fill:#10b981,color:#fff style DLQ fill:#fef3c7 style BUS fill:#e0f2fe
Azure Service Topology
On Azure, the event-driven pattern maps to three distinct managed services matched to three distinct flow types. Event Grid handles pub/sub routing of discrete domain events using CloudEvents schema with serverless per-operation pricing. Service Bus handles commands where ordered delivery, duplicate detection, and guaranteed processing are required — Premium tier is mandatory in production for private endpoints, zone redundancy, and session support. Event Hubs handles high-throughput telemetry and log aggregation using the Kafka protocol, with up to 90-day retention and full replay; partition count is fixed at creation and should be sized at 2× peak consumer parallelism.
%%{init:{'theme':'base','themeVariables':{'fontSize':'14px','fontFamily':'IBM Plex Sans, system-ui, sans-serif','primaryColor':'#DBEAFE','primaryTextColor':'#1e3a5f','primaryBorderColor':'#2563EB','lineColor':'#374151','clusterBkg':'#F9FAFB','clusterBorder':'#D1D5DB','edgeLabelBackground':'#FFFFFF'},'flowchart':{'curve':'orthogonal','padding':30,'nodeSpacing':65,'rankSpacing':75,'useMaxWidth':true}}}%% flowchart TD SOURCE[Application or Azure Service\nPublishes CloudEvents payload] subgraph EG["Azure Event Grid"] TOPIC[Custom Topic\nCloudEvents 1.0 schema\nPublic network disabled] FILTER[Event Subscription\nincludedEventTypes filter\nRetry: 30 attempts / 24h] end subgraph SB["Azure Service Bus Premium"] SBT[Topic\nrequiresDuplicateDetection\nsupportOrdering enabled] SBSUB[Subscription\nmaxDeliveryCount: 10\nlockDuration: PT5M] SBDLQ[Dead-Letter Subqueue\ndeadLetteringOn\nMessageExpiration] end subgraph EH["Azure Event Hubs"] EHN[Event Hubs Namespace\nKafka protocol enabled\nRetention: up to 90 days] EHP[Partitions\nFixed at creation\nSized 2× peak parallelism] end subgraph CONSUMERS["Consumers — Independent and Idempotent"] CS1[Azure Function\nDomain event handler\nIdempotency via Cosmos DB] CS2[Azure Function\nCommand processor\nOrdered per session] CS3[Azure Stream Analytics\nTelemetry aggregation\nReal-time windowing] end BLOB[Azure Blob Storage\nDead-letter archive\nEvent Grid DLQ destination] SOURCE --> TOPIC TOPIC --> FILTER FILTER -->|Route to queue| SBT SBT --> SBSUB SBSUB -->|Deliver| CS2 SBSUB -->|MaxDeliveryCount exceeded| SBDLQ SBDLQ -->|Archive| BLOB FILTER -->|Dead-letter| BLOB SOURCE --> EHN EHN --> EHP EHP --> CS3 CS1 --> BLOB style SOURCE fill:#4f8ef7,color:#fff style SBDLQ fill:#fef3c7 style BLOB fill:#fef3c7 style FILTER fill:#e0f2fe style EHN fill:#d1fae5
Implementation Guide
Bicep — Service Bus Premium Namespace, Topic, and Subscription
This module provisions a zone-redundant Service Bus Premium namespace using Managed Identity only (local auth disabled), a topic configured for duplicate detection and ordered delivery, and a subscription with dead-lettering and a 5-minute lock duration.
param location string = resourceGroup().location
param namespaceName string
param topicName string = 'orders'
param subscriptionName string = 'order-processor'
resource sbNamespace 'Microsoft.ServiceBus/namespaces@2022-10-01-preview' = {
name: namespaceName
location: location
sku: {
name: 'Premium'
tier: 'Premium'
capacity: 1
}
properties: {
minimumTlsVersion: '1.2'
zoneRedundant: true
disableLocalAuth: true // Managed Identity only — no SAS keys
}
}
resource sbTopic 'Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview' = {
parent: sbNamespace
name: topicName
properties: {
requiresDuplicateDetection: true
duplicateDetectionHistoryTimeWindow: 'PT10M'
supportOrdering: true
defaultMessageTimeToLive: 'P14D'
enablePartitioning: false // Partitioning incompatible with sessions
}
}
resource sbSubscription 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview' = {
parent: sbTopic
name: subscriptionName
properties: {
maxDeliveryCount: 10
deadLetteringOnMessageExpiration: true
lockDuration: 'PT5M'
defaultMessageTimeToLive: 'P14D'
}
}
Terraform equivalent: Use
azurerm_servicebus_namespacewithsku = "Premium"andzone_redundant = true,azurerm_servicebus_topicwithrequires_duplicate_detection = trueandsupport_ordering = true, andazurerm_servicebus_subscriptionwithmax_delivery_count = 10anddead_lettering_on_message_expiration = true.
Bicep — Event Grid Topic and Subscription with Dead-Letter Destination
This module provisions a custom Event Grid topic with CloudEvents 1.0 schema and public network access disabled, plus an event subscription that routes filtered events to a Service Bus queue and archives undeliverable events to Blob Storage.
param location string = resourceGroup().location
param topicName string
param serviceBusQueueId string
param deadLetterStorageAccountId string
param deadLetterContainerName string = 'event-grid-dlq'
resource eventGridTopic 'Microsoft.EventGrid/topics@2022-06-15' = {
name: topicName
location: location
properties: {
inputSchema: 'CloudEventSchemaV1_0'
publicNetworkAccess: 'Disabled'
disableLocalAuth: false
}
}
resource eventSubscription 'Microsoft.EventGrid/topics/eventSubscriptions@2022-06-15' = {
parent: eventGridTopic
name: 'order-events-to-servicebus'
properties: {
destination: {
endpointType: 'ServiceBusQueue'
properties: {
resourceId: serviceBusQueueId
}
}
filter: {
includedEventTypes: [
'com.ascendion.orders.created'
'com.ascendion.orders.updated'
]
}
retryPolicy: {
maxDeliveryAttempts: 30
eventTimeToLiveInMinutes: 1440
}
deadLetterDestination: {
endpointType: 'StorageBlob'
properties: {
resourceId: deadLetterStorageAccountId
blobContainerName: deadLetterContainerName
}
}
}
}
Terraform equivalent: Use
azurerm_eventgrid_topicwithinput_schema = "cloudeventschemaV1_0"andpublic_network_access_enabled = false, thenazurerm_eventgrid_event_subscriptionwithretry_policyblock (max_delivery_attempts = 30,event_time_to_live = 1440) and astorage_blob_dead_letter_destinationblock pointing to the archive container.
Consumer Idempotency Pattern
Event Grid and Service Bus both provide at-least-once delivery. Track processed event IDs in Azure Table Storage or Cosmos DB before executing business logic. The conditional insert approach mirrors the DynamoDB pattern on AWS.
public async Task ProcessOrderEventAsync(CloudEvent cloudEvent)
{
var eventId = cloudEvent.Id;
// Idempotency check — conditional insert fails if already processed
var entity = new TableEntity(partitionKey: "processed", rowKey: eventId)
{
["processedAt"] = DateTimeOffset.UtcNow,
["expiresAt"] = DateTimeOffset.UtcNow.AddDays(1)
};
try
{
await _tableClient.AddEntityAsync(entity); // Throws on duplicate key
}
catch (RequestFailedException ex) when (ex.Status == 409)
{
_logger.LogInformation("Event {EventId} already processed — skipping.", eventId);
return;
}
await ExecuteOrderBusinessLogicAsync(cloudEvent);
}
Decision Criteria
| Scenario | Recommended Service | Reason |
|---|---|---|
| State-change notifications, reactive integrations | Event Grid | Serverless per-operation pricing, CloudEvents schema, instant fan-out |
| Ordered command delivery, guaranteed processing | Service Bus Premium | Sessions, duplicate detection, dead-letter, zone redundancy |
| High-throughput telemetry, log aggregation | Event Hubs | Kafka protocol, 90-day retention, replay, partition-parallel consumers |
| Fan-out to multiple independent consumers | Event Grid → Service Bus subscriptions | Each consumer gets isolated subscription with own dead-letter |
| Ordered processing per entity | Service Bus with session ID | Groups all messages for an entity into a single ordered session |
| Cross-service domain event choreography | Event Grid + Service Bus | Event Grid routes; Service Bus delivers with guarantees |
| Real-time stream processing | Event Hubs + Stream Analytics | Native integration, windowed aggregation, no infrastructure |
| Replay of historical events | Event Hubs | Up to 90-day configurable retention with consumer-group offset reset |
Cost Model
| Service | Pricing Unit | Typical monthly cost at 10M events |
|---|---|---|
| Event Grid | $0.60 per million operations (after 100k free) | ~$6 |
| Service Bus Premium | Fixed $668/month per messaging unit (1 MU) | ~$668 base + $0 per message |
| Service Bus Standard | $0.013 per million operations | ~$0.13 (not for production) |
| Event Hubs Standard | $22.32/month per throughput unit + $0.028/GB | ~$25–80 depending on volume |
| Event Hubs Premium | Fixed per processing unit, higher retention | ~$730/month per PU |
| Blob Storage (dead-letter archive) | $0.018 per GB/month | ~$1 |
Cost optimisation levers:
- Use Service Bus Standard only in non-production environments; Premium is required for sessions, zone redundancy, and private endpoints in production
- Size Event Hubs partitions at creation to match peak parallelism — you cannot reduce partition count later without recreating the namespace
- Enable Event Hubs auto-inflate (Standard/Premium) to scale throughput units automatically rather than over-provisioning
- Set message TTL to the minimum needed on Service Bus topics — retained messages count toward namespace storage quota
- Archive Event Hubs cold data to Azure Data Lake Storage Gen2 with Capture rather than extending retention periods
Anti-Patterns to Avoid
Routing 10,000+ events per second through Event Grid for telemetry or log aggregation, treating it as a general-purpose streaming bus. Event Grid is priced per operation and optimised for discrete domain events, not continuous high-throughput streams.
Use Azure Event Hubs for telemetry and log aggregation. Event Hubs supports the Kafka protocol, scales to millions of events per second, and provides up to 90-day retention with replay. Event Grid handles domain state-change notifications; Event Hubs handles data streams.
Deploying Service Bus Standard in a production workload that requires session-based ordered processing, private endpoints, or zone redundancy. Standard tier does not support any of these capabilities.
Use Service Bus Premium for all production deployments. Premium provides zone redundancy, private endpoints, session support, and Managed Identity enforcement. The fixed per-messaging-unit cost is predictable and the operational guarantees are non-negotiable for production.
Creating Event Grid subscriptions or Service Bus topics with dead-letter destinations configured, but setting no Azure Monitor alert on the dead-letter count. Failed events accumulate silently until they expire or fill storage, with no signal to on-call engineers.
Create an Azure Monitor metric alert on DeadletteredMessageCount > 0 for every Service Bus subscription and a Log Analytics alert for Event Grid dead-letter blob writes. Treat every dead-lettered event as an incident requiring investigation.
Multiple independent applications reading from the same Event Hubs consumer group. Consumer group offset is shared, so applications compete for messages, skip events intended for each other, and corrupt each other's offset tracking.
Create a dedicated consumer group per application. Event Hubs supports up to 20 consumer groups per namespace (Standard) or more on Premium. Each application maintains its own offset and can replay independently.
Creating Service Bus topics without requiresDuplicateDetection = true when the producer may retry on network errors. A retry after a timeout can deliver the same command twice — placing the same order, charging the same payment — with no broker-side protection.
Enable requiresDuplicateDetection on Service Bus topics and ensure producers use a stable, idempotent MessageId (for example, a deterministic hash of the business key). The broker deduplicates within the configured history window (default 10 minutes).
Flowchart
References
- Microsoft — Azure messaging services comparison. https://learn.microsoft.com/azure/service-bus-messaging/compare-messaging-services
- Microsoft — Event Grid delivery and retry. https://learn.microsoft.com/azure/event-grid/delivery-and-retry
- Microsoft — Azure Service Bus Premium messaging. https://learn.microsoft.com/azure/service-bus-messaging/service-bus-premium-messaging
- Microsoft — Azure Event Hubs features. https://learn.microsoft.com/azure/event-hubs/event-hubs-features
- Microsoft — CloudEvents schema support in Azure Event Grid. https://learn.microsoft.com/azure/event-grid/cloud-event-schema
- Microsoft — Azure Well-Architected Framework — Reliability. https://learn.microsoft.com/azure/well-architected/reliability/
- Portal: AWS event-driven comparison