CH15-04: Consistency, Latency, and Event-Driven Trade-Offs
Goal
The previous section sketched a target architecture. This section explains one of its most important choices: what should happen in the customer request path and what should continue asynchronously.
The durable idea is:
Event-driven design helps commerce platforms absorb complexity, but it does not remove correctness responsibilities.
The Short Version
Asynchronous processing is valuable in commerce because many important tasks do not need to finish before the customer receives a response. Fulfillment handoff, notifications, analytics, search projections, support views, and some inventory workflows can continue after order creation.
But asynchronous does not mean vague. The platform still needs clear order state, idempotency, retry rules, duplicate handling, dead-letter queues, observability, ownership, and customer-visible status.
The design question is not "Should we use events?" The better question is:
Which decision must be made now, which work must complete later, and how will the system prove what happened?
The Checkout Split
Checkout is where the synchronous and asynchronous worlds meet.
Checkout request
-> validate identity, cart, price, availability, fraud, and payment boundary
-> create durable order state
-> return clear customer outcome
|
+-> durable fulfillment work
+-> notification work
+-> analytics and search projections
+-> support and tracking projections
The synchronous path should do enough to make a trustworthy customer decision. The asynchronous path should carry the rest of the work durably.
For example, a customer should not wait for every warehouse system, carrier system, email provider, analytics pipeline, and recommendation update before seeing an order confirmation. At the same time, the platform should not return success if it has not created durable order state.
What Should Stay Synchronous
Keep work synchronous when the customer response depends on it.
| Responsibility | Why It Usually Stays in the Request Path |
|---|---|
| Authenticate customer or caller | The platform must know who is acting. |
| Validate cart contents | The order should not be created from invalid or stale cart state. |
| Recalculate price, promotion, and tax inputs where needed | The customer needs a clear commercial decision. |
| Check fraud and payment authorization boundary | The platform should not create a normal confirmed order if payment or fraud state is unresolved. |
| Create order record or clear pending state | The platform needs durable evidence before it tells the customer what happened. |
| Return confirmation, rejection, or pending status | The customer needs an understandable outcome. |
There are exceptions. Some businesses support delayed payment capture, preorder, backorder, manual fraud review, or pending confirmation. Those are valid patterns, but the status must be explicit. Ambiguous success is dangerous.
What Can Continue Asynchronously
Move work asynchronous when it is important but does not need to block the immediate customer response.
| Responsibility | Why It Can Often Be Asynchronous | Required Control |
|---|---|---|
| Fulfillment handoff | Warehouse and delivery workflows may take time. | Durable message, status tracking, retry, owner. |
| Customer notification | Email or push providers can be slow or unavailable. | Retry, fallback, communication history. |
| Inventory workflow updates | Reservation, allocation, and replenishment may involve other systems. | Idempotent commands, workflow state, exception path. |
| Analytics | Reporting does not need to block checkout. | Event quality, replay, late-arrival handling. |
| Search and support projections | Read models can update after order creation. | Projection lag monitoring and repair path. |
This is where Service Bus, Event Grid, Azure Functions, Container Apps workers, Logic Apps, or durable workflow services may appear. The service choice depends on the communication contract.
Commands, Events, and Streams
Commerce designs often confuse communication styles.
| Style | Plain-Language Meaning | Commerce Example | Azure Fit |
|---|---|---|---|
| Command | Please do this work. | Fulfill order 123. | Service Bus queue or topic, workflow service. |
| Event | This fact happened. | Order 123 was created. | Event Grid or Service Bus topic, depending on durability and subscriber needs. |
| Stream | Many records for analysis or processing. | Clickstream, product views, telemetry. | Event Hubs or analytics ingestion path. |
| Workflow | Coordinate multiple steps over time. | Reserve inventory, create warehouse task, handle carrier exception. | Logic Apps, Durable Functions, or application workflow engine. |
The terms matter because failure behavior differs. A command that must complete needs a durable owner. An event may have many subscribers, and each subscriber owns its reaction. A stream is not a task queue. A workflow needs state and decisions over time.
Reliability Controls
Asynchronous commerce work needs reliability controls from the start.
| Control | What It Protects |
|---|---|
| Idempotency | The same request or message can be handled more than once without creating duplicate side effects. |
| Duplicate detection | Repeated messages or repeated sends do not create duplicate work. |
| Retry with backoff | Temporary failures are retried without overwhelming dependencies. |
| Dead-letter handling | Messages that repeatedly fail move to an investigation path. |
| Correlation IDs | One customer action can be traced across APIs, queues, workers, and logs. |
| Status model | Customers and support teams can see whether work is created, pending, failed, delayed, or complete. |
| Replay rules | Operators know when it is safe to reprocess messages or events. |
| Ownership | Someone is accountable for stuck queues, failed messages, and customer impact. |
Without these controls, asynchronous design merely moves confusion out of the request path.
Example: Notification Failure After Order Creation
A customer completes checkout. Payment authorization succeeds, the order record is created, and a message is placed on a queue for downstream processing. The email provider is unavailable when the notification worker tries to send confirmation.
The order should not disappear. The customer should still be able to see the order in order history. The notification worker should retry according to policy. If repeated attempts fail, the message should move to a dead-letter or exception path with enough context for investigation. Operators should see the failure rate and message age. Support agents should be able to tell the customer that the order exists even if the email is delayed.
That is the difference between reliable asynchronous processing and hopeful background work.
Common Mistakes
Using events as a way to avoid defining order state. Events describe changes; they do not replace a clear state model.
Ignoring idempotency and duplicate prevention. Customers notice duplicate orders, duplicate charges, and repeated notifications.
Letting downstream failures corrupt the customer-facing order experience. Failure should be visible and recoverable, not silently mixed into checkout success.
Treating every integration as an event. Commands, events, streams, and workflows solve different problems.
Creating dead-letter queues without alerts, dashboards, runbooks, or owners.
If Someone Asks You
I would keep checkout validation, payment boundary coordination, and durable order creation in the immediate request path. Then I would move fulfillment handoff, notifications, analytics, projections, and some inventory workflows to asynchronous processing. For work that must complete, I would use durable messaging such as Service Bus and design idempotency, retries, dead-letter handling, correlation IDs, replay rules, status visibility, and ownership.
What You Should Remember
Asynchronous processing protects the request path only when state, retries, and observability are designed.
Questions to Check Your Understanding
Which checkout responsibilities should stay synchronous?
Why does idempotency matter in order processing?
What should happen when a downstream notification fails?