
Picture this: your organization processes thousands of sales orders per day. Each order triggers a cascade of downstream operations — inventory checks, ERP updates, customer notifications, invoice generation, and regulatory compliance logging. You've built a Power Automate flow to handle this, and for the first few weeks, it hums along beautifully. Then Black Friday arrives. Order volume spikes 40x. Your flow starts timing out. Downstream APIs get hammered and begin rate-limiting responses. Some orders silently fail because a transient API error caused the flow to error out at step 3, and by the time anyone notices, the dead-letter audit trail is a spreadsheet someone emailed around. The entire automation collapses under its own weight precisely when it matters most.
This scenario isn't a cautionary tale — it's a pattern that repeats itself across enterprise automation projects because most practitioners build synchronous workflows when they should be building event-driven ones. The fundamental architectural mistake is coupling the producer of work (the order submission system) directly to the consumer of work (the processing pipeline). When one side hiccups, the other side suffers. When volume spikes, there's no buffer. When a downstream system goes offline for maintenance, you either block everything upstream or start losing events.
Azure Service Bus solves exactly this problem by sitting between producers and consumers as a durable, ordered message broker. Combined with Power Automate's ability to both publish to and consume from Service Bus queues and topics, you get an architecture that can handle massive throughput, guarantee delivery, and survive partial system failures without losing a single event. By the end of this lesson, you'll be able to design and implement that architecture from the ground up.
What you'll learn:
Before diving in, you should be comfortable with the following:
Configure run after.You'll need an Azure subscription with permissions to create Service Bus namespaces, and a Power Automate Premium license (the Service Bus connector is a Premium connector and requires Plan 1 or higher, or a Power Apps Premium license).
Before you write a single flow, you need to understand what Service Bus actually is and how it behaves under load — because the design decisions you make at the architecture level will either save you or haunt you at scale.
A queue is the simplest construct: messages go in one end, and one consumer (or a competing pool of consumers) reads them out the other end. Each message is delivered to exactly one consumer. This is your point-to-point pattern.
A topic works differently. Messages are published to the topic, and then Service Bus fans out copies of each message to one or more subscriptions. Each subscription maintains its own independent message queue. If you have three subscriptions on a topic, each published message results in three independent copies — one per subscription — and each subscription can have a completely different consumer. This is your publish-subscribe pattern.
For the order processing example: the order service publishes a single OrderCreated event to a topic. The InventorySubscription is consumed by a flow that reserves stock. The ERPSubscription is consumed by a flow that creates a sales order record. The NotificationSubscription is consumed by a flow that sends customer confirmation emails. All three run independently, in parallel, at their own pace, and a failure in the notification flow doesn't affect inventory reservation at all.
This is the most important behavioral detail to understand, and the one that causes the most bugs in Power Automate integrations.
When a consumer reads a message from Service Bus, it doesn't immediately disappear from the queue. Instead, Service Bus locks the message for a configurable period (the lock duration, which you set at the queue or subscription level, defaulting to 60 seconds). The message is invisible to other consumers while locked. The consuming application then has two options:
If the consumer does neither within the lock duration, Service Bus automatically releases the lock, the message becomes visible again, and the delivery count increments by 1. When the delivery count exceeds the max delivery count (configurable, defaulting to 10), Service Bus automatically moves the message to the dead-letter queue (DLQ) — a special sub-queue where unprocessable messages accumulate for inspection.
Why does this matter for Power Automate? Because Power Automate flows are not instantaneous. A flow that makes three API calls, writes to SharePoint, and sends an email might take 45-90 seconds. If your lock duration is 60 seconds and your flow takes 75 seconds, the lock expires mid-execution. Service Bus releases the message. Another flow run picks it up. Now you have two flow instances processing the same order. Congratulations, you've just double-charged a customer.
Critical warning: Always set your lock duration significantly longer than your expected maximum flow execution time. For flows that might run 2-5 minutes, set lock duration to 10 minutes. For flows that call slow external APIs, go longer. You can set lock duration up to 5 minutes on Standard tier and up to 5 minutes by default, but with sessions and the right configuration you can extend further. Check the current Azure Service Bus documentation for tier-specific limits, as these change.
Sessions are a Service Bus feature that guarantees ordered processing for messages that share a common identifier — a SessionId. If you need to guarantee that all events for a specific customer, order, or entity are processed in the sequence they were emitted, you use sessions.
When you enable sessions on a queue, every message must have a SessionId set. Service Bus groups messages by SessionId and guarantees that within a session, only one consumer processes messages at a time, and in order. This is critical for scenarios like event sourcing, where you might have OrderCreated, OrderUpdated, and OrderShipped events for the same order, and processing them out of sequence would corrupt your state.
Power Automate's Service Bus connector does support session-based triggers, but with important caveats we'll cover when we get to the consumer flow design.
Azure Service Bus comes in two tiers that matter for enterprise workloads:
Standard tier gives you shared infrastructure, pay-per-operation pricing, and a maximum message size of 256 KB. For most Power Automate-driven workflows where message payloads are JSON event envelopes (rarely exceeding a few KB), Standard is perfectly adequate and much cheaper.
Premium tier gives you dedicated capacity (messaging units), predictable latency, support for message sizes up to 100 MB, and VNet integration for private networking. If you're processing tens of thousands of messages per second, storing large binary payloads in messages (don't — use the claim-check pattern instead), or have strict network isolation requirements, Premium is your tier.
For most Power Automate workflows, Standard tier is the right choice. Power Automate's consumption-based polling model means you're unlikely to saturate Standard tier throughput with flows. Where you'll hit limits first is actually Power Automate's own throttling, not Service Bus.
Resist the temptation to open the Azure portal before you've sketched out your architecture on paper (or in Visio, or in Miro — whatever your preference). The decisions you make here propagate through everything that follows.
Every message you publish to Service Bus should follow an envelope-payload pattern. The envelope contains routing and metadata information; the payload contains the business data. Here's a realistic example for our order processing scenario:
{
"envelope": {
"eventType": "OrderCreated",
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"correlationId": "WEB-SESSION-abc123",
"sourceSystem": "ECommercePortal",
"publishedAt": "2024-11-29T14:32:00Z",
"schemaVersion": "2.1"
},
"payload": {
"orderId": "ORD-2024-98765",
"customerId": "CUST-44321",
"lineItems": [
{ "sku": "PROD-7821", "quantity": 2, "unitPrice": 149.99 },
{ "sku": "PROD-3304", "quantity": 1, "unitPrice": 89.50 }
],
"shippingAddress": {
"street": "742 Evergreen Terrace",
"city": "Springfield",
"state": "IL",
"zip": "62701"
},
"totalAmount": 389.48,
"currency": "USD"
}
}
The eventId is a UUID you generate at publish time — this is your idempotency key. If a message is redelivered (because a lock expired or processing failed), your consumer can check whether it's already processed this eventId and skip it. Store processed event IDs in a fast lookup store — Azure Table Storage works well for this because it's cheap and has millisecond read latency.
The correlationId ties this event back to whatever initiated the original user action. This is invaluable for distributed tracing when things go wrong.
The schemaVersion field is something most people skip and almost everyone regrets skipping. Your event schema will change over time. When you're processing messages that were published yesterday with schema v2.0 while your consumer was updated to expect v2.1, you need to know how to handle the difference.
Start by listing every discrete business event your system generates. For an e-commerce platform, that list might look like:
OrderCreatedOrderUpdatedOrderShippedOrderCancelledPaymentProcessedPaymentFailedInventoryLowCustomerRegisteredNow, decide whether to use one topic per event type or one topic for all events with subscription filters.
One topic per event type is simpler to reason about, easier to set per-topic lock durations and retention policies, and makes access control cleaner (you can grant a service principal access to just the order-created topic). The downside is namespace clutter and a larger number of resources to manage.
One topic with subscription filters means publishing all events to a single topic (say, business-events) and using Service Bus's SQL filter or correlation filter capabilities to route messages to the right subscriptions. A subscription for inventory management might have a filter like eventType = 'OrderCreated' OR eventType = 'OrderCancelled'. This reduces the number of topics dramatically but makes routing logic harder to visualize and test.
My recommendation for most enterprise scenarios: use a small number of domain-scoped topics rather than a single mega-topic or a topic-per-event proliferation. For our e-commerce example: orders-events, payments-events, inventory-events, customer-events. This groups related events, keeps filter complexity manageable, and allows per-domain access control.
Service Bus messages have a hard size limit (256 KB on Standard, 1 MB on Premium by default, configurable to 100 MB on Premium). If your event payloads are large — say, a product catalog update with hundreds of line items, or a document processing event that includes a binary attachment — you'll hit this limit.
The solution is the claim-check pattern: store the large payload in Azure Blob Storage, and put only a reference (the "claim check") in the Service Bus message. The consumer retrieves the actual payload from blob storage using the reference.
{
"envelope": {
"eventType": "CatalogUpdated",
"eventId": "7f3e9a2b-1c4d-4e5f-8a6b-9d0e1f2a3b4c",
"sourceSystem": "ProductManagementSystem",
"publishedAt": "2024-11-29T15:00:00Z"
},
"claimCheck": {
"storageAccount": "mycompanydatastore",
"container": "event-payloads",
"blobName": "catalog-updates/2024-11-29/7f3e9a2b.json",
"expiresAt": "2024-12-06T15:00:00Z"
}
}
In Power Automate, your consumer flow would read the claimCheck object, use the Azure Blob Storage connector to retrieve the full payload, process it, and then delete the blob after successful completion.
Now we'll actually configure the infrastructure.
In the Azure portal, navigate to Create a resource, search for Service Bus, and click Create. You'll configure:
contoso-integration-bus rather than sb-dev-01.After creation, navigate to your namespace and select Shared access policies. By default, there's a RootManageSharedAccessKey policy with full access. Do not use this in production flows. Instead, create purpose-specific policies:
orders-producer: Send permission only, for systems that publish to the orders topicorders-consumer: Listen permission only, for Power Automate flows that consume from orders subscriptionsorders-admin: Send + Listen + Manage, for administrative toolingNavigate to Topics in your Service Bus namespace and click + Topic. Create orders-events with the following settings:
Tip: Duplicate detection at the broker level uses the message's
MessageIdproperty. If you set a deterministicMessageId(such as theeventIdfrom your envelope) and a producer accidentally publishes the same event twice within the detection window, Service Bus will silently discard the duplicate. This is your first line of idempotency defense.
After creating the topic, click into it and create subscriptions. For our order processing scenario:
Subscription: inventory-management
Subscription: erp-sync
Subscription: customer-notifications
For each subscription that needs filters, click + Add filter after creating the subscription. Use a SQL filter expression like:
eventType IN ('OrderCreated', 'OrderUpdated')
Wait — that syntax won't work directly with Service Bus SQL filters because the filter operates on message properties, not the message body. This is a critical distinction. Service Bus filters can only inspect message system properties (like MessageId, ContentType, SessionId) and custom user properties (also called application properties) that you set at publish time — not the JSON body of the message.
This means when you publish to Service Bus, you need to set user properties that mirror the key routing fields from your envelope. In Power Automate's Service Bus connector, the Send message action has a Properties section where you can set key-value pairs. Set eventType as a user property, and your subscription filters will work correctly.
Let's build a flow that publishes an OrderCreated event to Service Bus. The trigger in this scenario is a new row being created in a PendingOrders table in Azure SQL Database.
Trigger: SQL Server — When an item is created (or use the recurrence trigger with a polling query for more control)
Step 1: Generate correlation metadata
Use the guid() expression to generate a unique eventId:
guid()
Store this in a variable called varEventId. You'll use this as both the message body's eventId and the Service Bus MessageId property.
Step 2: Construct the message body
Use a Compose action to build the JSON envelope:
{
"envelope": {
"eventType": "OrderCreated",
"eventId": "@{variables('varEventId')}",
"correlationId": "@{triggerBody()?['SessionId']}",
"sourceSystem": "SalesOrderSystem",
"publishedAt": "@{utcNow()}",
"schemaVersion": "2.1"
},
"payload": {
"orderId": "@{triggerBody()?['OrderId']}",
"customerId": "@{triggerBody()?['CustomerId']}",
"totalAmount": "@{triggerBody()?['TotalAmount']}",
"currency": "@{triggerBody()?['Currency']}",
"lineItems": @{triggerBody()?['LineItemsJson']}
}
}
Step 3: Send to Service Bus
Use the Send message action from the Service Bus connector. Configure:
orders-producer SAS keyorders-events@{variables('varEventId')} — this enables broker-level duplicate detection@{triggerBody()?['CustomerId']} — if you've enabled sessions, use a meaningful entity IDapplication/jsoneventType, Value: OrderCreatedsourceSystem, Value: SalesOrderSystemschemaVersion, Value: 2.1Step 4: Update source record status
After successful publish, update the PendingOrders record to set EventPublished = true and PublishedAt = utcNow(). This prevents republishing if your flow runs again before the trigger polls again.
Warning: Don't update the source record before the Service Bus send. If the send fails, you'll have marked the order as published when it wasn't. Always update status after the operation that status is tracking.
Wrap the Send message action in a Scope and configure a parallel error-handling scope that runs if the primary scope fails. In the error handler:
PublishErrors table with the order ID, error message, and timestampThe retry pattern in Power Automate for transient failures looks like this:
varRetryCount to 0 and varSuccess to falsevarSuccess = true or varRetryCount >= 3:varSuccess = truevarRetryCount, then use Delay action for mul(variables('varRetryCount'), 30) seconds (30s, 60s, 90s backoff)varSuccess = false, fire the alerting logicThis is manual and verbose, but it's the explicit, auditable approach Power Automate requires.
The consumer is where most of the architectural complexity lives. Getting this wrong leads to duplicate processing, message loss, or flows that hammer Service Bus with infinite retries.
The Service Bus connector offers two trigger types for consuming messages:
"When a message is received in a queue (auto-complete)": Service Bus marks the message as complete immediately when your flow starts. This is fast and simple, but it means if your flow fails halfway through, the message is gone. Use this only for idempotent, low-stakes operations where message loss is acceptable.
"When a message is received in a queue (peek-lock)": Service Bus locks the message but doesn't complete it. Your flow must explicitly complete, abandon, or dead-letter the message. This is the correct choice for any workflow where reliability matters.
Use the peek-lock trigger. Always. The auto-complete trigger is a footgun at enterprise scale.
When you configure the peek-lock trigger, you'll see a Lock token property in the trigger output. This token is your handle to the locked message. You need this token for every subsequent Service Bus action (Complete, Abandon, Dead-letter).
Trigger: Service Bus — When a message is received in a subscription (peek-lock)
Configure:
orders-eventsinventory-managementStep 1: Parse the message body
The message body arrives base64-encoded. Use the base64ToString() expression to decode it, then json() to parse:
json(base64ToString(triggerBody()?['ContentData']))
Use a Parse JSON action with your schema to get typed access to all envelope and payload fields. Generate the schema from a sample message body using the Generate from sample button.
Step 2: Idempotency check
Before doing any real work, check whether you've already processed this eventId. Query your Azure Table Storage ProcessedEvents table:
ProcessedEvents eventId (for partition distribution)eventIdIf the record exists, the message is a duplicate. Go directly to Step 6 (Complete the message) without processing. This handles the case where your lock expired and Service Bus redelivered a message you'd already processed.
Step 3: Execute business logic in a Scope
Wrap all your actual processing steps inside a Scope action called "Process Order for Inventory." Inside this scope:
Keep this logic modular. If you have multiple API calls, wrap each one in its own inner scope with appropriate error handling so you can distinguish which step failed in your error logs.
Step 4: Record the processed event
After successful processing but before completing the message, write to your ProcessedEvents table:
eventIdeventId{ "processedAt": "<utcNow>", "orderId": "<orderId>", "outcome": "success" }Step 5: Configure run after for error handling
Add a parallel branch that runs if the main Scope has failed or timed out. In this error handler:
Check the delivery count of the message using the trigger output:
triggerBody()?['DeliveryCount']
If the delivery count is less than your max delivery count minus 1 (leave one attempt for the DLQ transition), use the Abandon message in a queue action (providing the lock token). This returns the message to the queue for retry.
If the delivery count has reached or exceeded the threshold, use the Dead-letter message in a queue action with a meaningful reason:
MaxProcessingAttemptsExceededresult('Scope_ProcessOrder')?[0]?['error']?['message']Step 6: Complete the message
In the main (success) path, use Complete the message in a queue with the lock token. This permanently removes the message from the subscription.
Critical implementation detail: The lock token is only valid during the flow run. You cannot store the lock token and use it in a different flow run. Every peek-lock trigger run has its own lock token for its own locked message.
For flows that run close to the lock duration boundary, you need to renew the lock mid-execution. Power Automate's Service Bus connector has a Renew message lock in a queue action. Use this inside a Do Until loop running in parallel with your processing logic... except Power Automate doesn't natively support true parallelism within a single flow run.
The practical solution is to architect your long-running processing into segments. After each segment completes, renew the lock before proceeding to the next segment. It's inelegant but reliable:
Alternatively — and this is often the cleaner architectural choice — if your processing logic genuinely takes longer than a few minutes, consider whether Service Bus + Power Automate is the right tool for that specific step, or whether you should trigger a Power Automate child flow or an Azure Function from the consumer flow to handle the long-running work.
Let's revisit the subscription filter topic with a concrete scenario. Suppose your orders-events topic receives messages for OrderCreated, OrderUpdated, OrderShipped, and OrderCancelled events. Your inventory management subscription only needs to respond to OrderCreated and OrderCancelled.
In the Azure portal, navigate to your subscription, select Filters, delete the default TrueFilter, and add a new SQL filter:
eventType = 'OrderCreated' OR eventType = 'OrderCancelled'
Remember: eventType must be set as a user property (not just in the JSON body) when publishing. Go back to your publisher flow and verify the user property is being set correctly.
Correlation filters are more performant than SQL filters for simple key-value matching because Service Bus evaluates them without a SQL parser. If your routing logic is simple equality matches, prefer correlation filters. For complex logic (IN clauses, LIKE patterns, numeric comparisons), SQL filters are your only option.
A common mistake is setting up filters and assuming they work. Test explicitly:
eventType = 'OrderCreated' — verify it arrives in the inventory-management subscriptioneventType = 'OrderShipped' — verify it does NOT arrive in inventory-managementThe dead-letter queue is not where messages go to die — it's where they go for human intervention. A well-designed system treats the DLQ as an operational concern that gets monitored and actioned, not ignored.
Create a separate Power Automate flow on a recurrence trigger (every 15 minutes) that:
GET https://<namespace>.servicebus.windows.net/<topic>/subscriptions/<subscription>/$deadletterqueue?api-version=2017-04
You'll need to authenticate this with a SAS token, which you can generate using Power Automate's built-in expression capabilities or by using the HTTP action with appropriate headers.
For messages that landed in the DLQ due to transient failures (a dependent system was temporarily offline), you'll want a mechanism to replay them. Build a flow triggered manually (instant trigger with no inputs) or via an HTTP trigger that:
<subscription>/$deadletterqueue)DeadLetterReason propertyWarning: Never blindly replay all DLQ messages automatically. Some messages may be in the DLQ because they contain genuinely invalid data that will cause every consumer to fail. Automatic replay without human inspection will cause an infinite loop of failure → DLQ → replay → failure. Always add human review or at minimum a DLQ-reason filter before automated replay.
Let's be direct about where this architecture works brilliantly and where it struggles.
Power Automate flows are throttled at multiple levels:
For a peek-lock consumer flow, each message consumes one concurrent run slot for the duration of message processing. If your processing takes 60 seconds per message and you have 25 concurrent runs, you can process roughly 25 messages per minute — 1,500 messages per hour. For many enterprise workflows, this is more than sufficient. For high-throughput scenarios (tens of thousands of events per hour), you'll need to either:
Horizontally scale with multiple environments: Deploy the same consumer flow across multiple Power Platform environments, each with its own Service Bus connection. Service Bus will distribute messages across all competing consumers.
Hybrid architecture: Use Power Automate for orchestration and Azure Functions or Logic Apps Standard for high-throughput message processing. Power Automate publishes to Service Bus; Azure Functions consume at scale; Power Automate handles the downstream human-centric workflows (approvals, notifications, SharePoint updates).
Batch processing: Configure the consumer trigger to receive multiple messages per run (increase Maximum message count to 10 or 20). In the trigger output, messages arrive as an array. Use an Apply to each loop to process them sequentially, completing or dead-lettering each one individually. This reduces the overhead of flow initialization but makes error handling more complex because you need to track which messages in the batch succeeded and which failed.
Power Automate's Service Bus trigger uses polling, not a push subscription. The connector polls Service Bus on a schedule — approximately every 30 seconds for Standard plan, potentially less frequent in some configurations. This means there's inherent latency between when a message arrives in the queue and when your flow starts processing it.
For most business workflows, 30-second latency is completely acceptable. If you're building a real-time user-facing experience where sub-second latency matters, Power Automate is not the right consumer — use Azure Functions with a Service Bus trigger (which uses the Service Bus SDK's long-polling for near-real-time trigger response).
For everything else — inventory updates, ERP sync, notification delivery, audit logging — 30-second latency is a non-issue.
If your flow runs in Power Automate connected to Azure resources that support managed identity (like Azure Logic Apps Standard or custom connectors backed by Azure API Management), prefer managed identity over SAS keys. SAS keys are strings that can be leaked, accidentally committed to source control, or captured in flow run history.
For standard Power Automate connector connections, you'll use SAS keys. Protect them:
By default, Power Automate stores the inputs and outputs of every action in flow run history. For a Service Bus consumer, this means the full message body is visible in run history to anyone with flow edit permissions. For sensitive payloads (PII, financial data, health records), this is a compliance problem.
Mitigate this by:
For Premium tier Service Bus, enable Private Endpoints to ensure that Service Bus is not reachable from the public internet. Traffic flows through Azure's private network backbone. Power Automate's Service Bus connector can reach private endpoints if you're using an on-premises data gateway or if your Power Platform environment is configured with a virtual network data gateway.
For Standard tier, at minimum configure IP filtering on the Service Bus namespace to allowlist the IP ranges used by Power Automate (published in Microsoft's IP range documentation for the Power Platform data centers in your region).
Build the following complete, working system in your own Azure subscription and Power Automate environment.
Scenario: An HR system generates EmployeeOnboarded events that must trigger two independent downstream workflows: (1) provision access to company systems via an API, and (2) create a welcome notification record in SharePoint.
Part 1: Infrastructure Setup
<yourname>-hr-integration on Standard tierhr-events with duplicate detection enabled (10-minute window)system-provisioning and employee-notificationssystem-provisioning, add a SQL filter: eventType = 'EmployeeOnboarded'employee-notifications, add a SQL filter: eventType = 'EmployeeOnboarded'hr-producer (Send) and hr-consumer (Listen)Part 2: Publisher Flow
eventType as a user propertyMessageId to a generated GUIDhr-events topic using the hr-producer connectionPart 3: Consumer Flow — System Provisioning
system-provisioning subscriptionhttps://httpbin.org/post (a useful testing endpoint)Part 4: Consumer Flow — Employee Notifications
employee-notifications subscriptionVerification: Trigger your publisher flow with a test employee record. Verify that:
Mistake 1: Using auto-complete trigger in production Messages disappear the moment your flow starts. Any failure in your flow logic means permanent message loss. Switch to peek-lock and implement explicit completion.
Mistake 2: Lock expiration causing duplicate processing Symptom: your ERP has duplicate order records, or customers receive double notifications. Fix: extend lock duration, implement idempotency checks, renew locks mid-processing.
Mistake 3: Filtering on message body instead of user properties Your subscription filter doesn't match any messages, so the subscription queue is always empty. Fix: set user properties in the publisher and verify with Service Bus Explorer.
Mistake 4: Not handling the DLQ Messages accumulate in the DLQ silently. Weeks later, someone notices that 3,000 orders were never processed. Fix: build DLQ monitoring and alerting from day one, not as an afterthought.
Mistake 5: Storing the lock token across flow runs The lock token is only valid in the context of the flow run that received it. If you try to store it in a variable and use it later, the complete/abandon action will fail with a 410 Gone error. Each flow run locks and must complete/abandon its own messages.
Mistake 6: Setting max delivery count too high With max delivery count of 10, a poison message will cause your consumer flow to run 10 times before going to the DLQ. If each run takes 2 minutes (mostly waiting for timeouts), that's 20 minutes of wasted execution time per poison message. Set max delivery count low (3-5) for most scenarios unless your retry logic has genuine value.
Mistake 7: Publishing to Service Bus synchronously in a user-facing request If Service Bus has a transient outage and your web app synchronously publishes to Service Bus before responding to the user, the user gets an error. Use a local queue or database as an intermediate buffer (the Outbox pattern), and have a reliable background process publish from the outbox to Service Bus. Power Automate can be that background process.
You've now built the conceptual and practical foundation for enterprise-grade event-driven automation using Power Automate and Azure Service Bus. Let's consolidate what you've covered:
The core architectural principle is decoupling via a durable message broker. Producers publish events without knowing or caring who consumes them. Consumers process at their own pace without blocking producers. Service Bus guarantees delivery, handles retries, and provides a safety net in the dead-letter queue.
The technical fundamentals that make this resilient are: peek-lock semantics (messages aren't acknowledged until processing succeeds), idempotency checks (duplicate deliveries don't cause duplicate processing), envelope-payload separation (messages carry routing metadata separately from business data), and DLQ monitoring (failures are visible and actionable, not silent).
The architecture scales through competing consumers (multiple flow instances across environments reading from the same subscription), topic fan-out (one publish triggers multiple independent subscriptions), and hybrid approaches (Power Automate for orchestration and human workflows, Azure Functions or Logic Apps for high-throughput raw processing).
Where to go from here:
correlationId thread.The patterns you've learned here extend far beyond Service Bus. The principles of decoupling, idempotency, durable delivery, and dead-letter management apply to any message broker — RabbitMQ, Kafka, AWS SQS, Google Pub/Sub. You're now thinking at the level of distributed systems design, not just flow building.
Learning Path: Flow Automation Basics