Contoso Distribution - SAP Order Line Ingress
Contoso Distribution receives order confirmations from SAP S/4HANA. At peak, SAP sends 6,000 order lines per hour. Each line must land in Dataverse as an Order Line row under the correct Order. SAP guarantees at-least-once delivery, retries on timeout, and can resend the same line hours later. Lines for a single order must be applied in the sequence SAP sent them. Once a line is stored, the warehouse system must be told exactly once, or the warehouse double-ships. The first design uses one Power Automate cloud flow per table with a Dataverse 'row added, modified or deleted' trigger plus an HTTP action to the warehouse. After go-live, runs queue for 40 minutes, lines arrive out of order, and the warehouse reports duplicates.
1. Explain why the trigger-based flow design cannot meet the requirements and propose a replacement architecture.
Read the worked answer
The design fails on four independent axes, which is why tuning it won't fix it:
- Throttling. Every Dataverse write consumes service protection capacity (roughly 6,000 requests per 5 minutes and 52 concurrent requests per user). A trigger that reads the change plus a flow that updates a status column multiplies the request count per inbound line; 6,000 lines/hour with 3–4 requests each sits close to the ceiling.
- Flow run limits. Per-user request entitlements are consumed by every run and every action; a sustained integration stream exhausts them and then stalls.
- Ordering. Dataverse triggers carry no ordering guarantee and flow retries reorder work; nothing binds a line to its predecessor.
- No delivery contract. Flow retries plus at-least-once SAP delivery produce duplicates by construction.
Replacement: SAP → Azure Service Bus queue (or topic) with sessions keyed on Order ID → consumer (Azure Function or Logic Apps Standard) authenticating with an Entra ID service principal → Dataverse Web API upsert using an alternate key on the line's SAP identifier → write to an outbox table in the same transaction → a dispatcher notifies the warehouse.
This decouples ingress rate from Dataverse limits, gives FIFO per order via sessions, and moves retries into a durable queue instead of a flow run.
2. How do you satisfy 'in sequence per order' and 'exactly once to the warehouse'?
Read the worked answer
Treat the two requirements separately; they are solved by different mechanisms.
Sequence per order
- Use Service Bus sessions with the Order ID as the session key and one active consumer per session. This gives ordered, single-threaded processing per order while still scaling across orders.
- Make the Dataverse write a single upsert per message. If an order update requires a parent row, create the parent idempotently first so ordering isn't violated by a race.
Exactly once to the warehouse
- End-to-end exactly-once messaging is not achievable; design for at-least-once delivery plus an idempotent consumer.
- Persist a dedupe record keyed on the SAP line identifier plus version, written in the same Dataverse transaction as the line upsert. If the dedupe record already exists, acknowledge and skip.
- Use an outbox row with a state column (pending/sent/confirmed) and a dispatcher that re-sends until the warehouse confirms. The warehouse must also dedupe on the same business key - put that in the integration contract.
- Never acknowledge the Service Bus message before the Dataverse transaction commits; otherwise a crash loses work.
3. What identity and licensing decisions do you make for the integration components?
Read the worked answer
Register an application in Entra ID and create a Dataverse application user with a custom security role. Application users are not interactive users, so the per-user Power Apps licenses held by Contoso staff are not consumed by the integration - but grant only the privileges it needs (Create/Write/Read on Order Line, Order and the dedupe/outbox tables), not System Administrator. Broad roles on an integration identity are the most common audit finding in these designs.
Decide deliberately between two consumer runtimes:
- Azure Function calling the Web API directly - no Power Platform connector licensing, full control of batching and retry, but you own the code.
- Power Automate / Logic Apps - lower code, but requires premium licensing and inherits the throttling characteristics you were trying to escape.
Because the volume requirement already forced you off per-row flows, the Function path is the consistent choice. Also confirm the tenant's service protection and API entitlement limits with the platform team and set the queue consumer's concurrency so parallel processing of different orders stays well inside those limits.