Skip to content

Core lesson

Event-Driven Order Processing

Evolve synchronous checkout into durable order acceptance, event routing, buffered consumers, coordinated fulfillment, and retry-safe processing.

15 min read

After this, you will understand

Learn to separate five jobs that are often blurred together: accepting business state, routing facts, buffering work, coordinating steps, and notifying subscribers.

Article guideprerequisites, mental models, and concepts

Article overview

intermediateCloudCertificationReliability

Three useful mental models

In plain terms

Store the order first, publish an OrderCreated fact, route it with EventBridge, buffer independent workers with SQS, coordinate ordered fulfillment with Step Functions, and make every side effect retry-safe.

Decision pressure

A synchronous dependency chain blocks checkout, a database write and event publish drift apart, one queue is mistaken for fanout, or retries repeat payments and inventory changes.

Exam-ready model

Create a durable acceptance boundary, publish reliable business events, give each consumer its own delivery shape, and design duplicates, poison messages, backpressure, and compensation explicitly.

Think before reading

Why does an at-least-once message consumer need idempotency?

Because the same message can be delivered again after a timeout or failure, and repeating the handler must not repeat the business effect.

Connected learning

These lessons add useful context to the current core lesson.
  1. 1Secure Partner File Ingest On S3AWS Scenario

Concepts Covered

  • Synchronous versus asynchronous order work
  • Durable order acceptance
  • The database-write/event-publish gap
  • EventBridge event buses and rules
  • SQS buffering, visibility timeout, and backpressure
  • Lambda polling and partial batch failure handling
  • At-least-once delivery and idempotent consumers
  • Dead-letter queues and replay
  • Step Functions orchestration and compensation
  • SNS notification fanout
  • AWS Solutions Architect Associate exam (SAA-C03) messaging recognition patterns

1. Situation

The cart API from the previous study now accepts checkout requests.

After an order is accepted, the business wants to:

  1. reserve inventory;
  2. authorize payment;
  3. arrange fulfillment;
  4. email or text the customer;
  5. update analytics; and
  6. notify fraud and operations systems.

Only one outcome must complete before the checkout API responds:

The order must be durably accepted, or the client must receive a clear failure.

Analytics can be late. Email can be retried. A temporary shipping outage should not prevent new orders from being accepted. Payment and inventory need more careful coordination because one may succeed while the other fails.

This creates a new architecture problem:

synchronous acceptance -> asynchronous fulfillment and side effects

The services overlap at the edges, but they do not have the same central job.

2. Naive Design

Naive option 1: keep the entire order inside one request

flowchart LR
  Client["Client"] --> API["Checkout API"]
  API --> Inventory["Inventory"] --> Payment["Payment"] --> Shipping["Shipping"] --> Email["Email"] --> Analytics["Analytics"]

The code is easy to trace when everything works. In production, every dependency becomes part of checkout availability and latency.

If analytics pauses for thirty seconds, the customer waits. If email fails, the system must decide whether to reject an otherwise valid order. If the client retries after a timeout, the entire chain may run again.

Naive option 2: publish an event before saving the order

Consumers receive OrderCreated, but the order database write then fails. They try to process an order that does not exist.

Naive option 3: save the order, then publish separately

1. database write succeeds
2. process crashes
3. event publish never happens

The customer sees an accepted order, but fulfillment never learns about it. This is the dual-write gap: two systems changed by two separate operations cannot be assumed to succeed atomically.

Naive option 4: use one SQS queue for every subscriber

Payment, analytics, and email workers all poll the same queue. Each message is normally processed by one competing consumer, so the services divide the messages instead of each receiving a copy.

A work queue is not automatically pub/sub fanout.

3. What Breaks

Follow four failure stories.

Failure 1: shipping is unavailable

In the synchronous chain, checkout fails even though the company could safely accept the order and arrange shipping later.

The architecture needs a durable asynchronous boundary so one consumer's outage does not stop order intake.

Failure 2: a payment response is lost

The payment provider authorizes the charge, but the worker times out before recording success. The message returns and is processed again.

At-least-once delivery needs an idempotency key such as the order ID or payment operation ID. Retrying transport must not mean charging twice.

Failure 3: one malformed message fails every batch

A Lambda polls ten SQS messages. Nine succeed and one malformed message fails. If the integration treats the whole batch as failed, all ten become visible again and repeat work.

The architecture needs partial batch failure handling and a dead-letter path for poison messages.

Failure 4: the queue keeps growing quietly

SQS preserves the work while a downstream service slows down. That prevents immediate loss, but it also hides a widening business delay unless the team monitors the age of the oldest message.

A queue is a buffer, not a cure. The architecture needs backpressure-aware concurrency and operational alarms.

4. AWS Architecture

Build the system by communication job.

Step 1: create a durable order-acceptance boundary

The checkout API validates the request and durably stores the order before reporting success.

flowchart LR
  Client["Client"] --> API["Checkout API"]
  API --> Store["Order store<br/>durable acceptance"]
  Store -->|"Order accepted"| API --> Client

The system must also preserve the intent to publish OrderCreated. Common solutions include:

  • a transactional outbox, where the order and an outbox record are written in one database transaction and a relay publishes the event; or
  • a database change stream that reliably exposes the committed change to a publisher.
flowchart LR
  API["Order service"] -->|"One supported transaction"| State["Order + publish intent"]
  State --> Relay["Outbox or change-stream relay"]
  Relay --> Event["OrderCreated event"]

This complexity exists to prevent the gap where business state commits but the event disappears. Review the Outbox Pattern when this boundary needs deeper implementation detail.

For the rest of the lesson, assume OrderCreated represents a committed business fact, not a hopeful command sent before storage succeeds.

Step 2: use EventBridge to route the business fact

Publish the event to an EventBridge event bus. A rule matches fields such as event source and detail type, then sends the event to a target.

flowchart LR
  Event["OrderCreated"] --> Bus["EventBridge event bus<br/>routing desk"]
  Bus -->|"Fulfillment rule"| Workflow["Fulfillment target"]
  Bus -->|"Analytics rule"| Analytics["Analytics target"]
  Bus -->|"Notification rule"| Notify["Notification target"]

EventBridge is useful when producers should not know every consumer and routing depends on event content.

Configure target retry policy and an EventBridge dead-letter queue when undeliverable target events must be retained after delivery attempts are exhausted.

That DLQ answers:

Could EventBridge deliver the event to its configured target?

It is different from a consumer queue's DLQ, which answers whether a delivered work item repeatedly failed during processing.

Step 3: put SQS in front of independent workers that need buffering

Give each independent consumer its own queue when it needs durable backlog, controlled processing speed, or isolation from outages.

flowchart LR
  Bus["EventBridge"] -->|"Analytics rule"| AnalyticsQ["Analytics SQS queue"]
  Bus -->|"Fraud rule"| FraudQ["Fraud SQS queue"]
  AnalyticsQ --> AnalyticsWorker["Analytics worker"]
  FraudQ --> FraudWorker["Fraud worker"]

Each queue receives the events for its consumer. Workers for the same queue compete to process its messages; separate queues let independent services each receive and retain their own copy.

SQS also creates backpressure. A downstream service can process at its safe rate while new messages wait durably in the queue.

Step 4: process SQS messages with retry safety

Lambda uses an event source mapping to poll SQS and invoke the function with a batch of messages.

flowchart LR
  Queue["SQS queue<br/>durable inbox"] -->|"Lambda polls a batch"| Worker["Lambda consumer"]
  Worker -->|"Success: message removed"| Done["Business effect recorded"]
  Worker -.->|"Failure: visible again later"| Queue
  Queue -->|"Too many failed receives"| DLQ["Consumer DLQ"]

When a message is received, SQS hides it for the visibility timeout. If processing succeeds, it is deleted. If processing does not complete successfully, it can become visible and be delivered again.

Therefore:

  • set the visibility timeout to cover expected processing and retry behavior;
  • make the consumer idempotent;
  • use partial batch responses so successful records are not retried merely because another record failed; and
  • send repeatedly failing messages to a DLQ with enough context for diagnosis and replay.

The DLQ is an isolation area, not automatic resolution. Someone or something must inspect, fix, and redrive the messages deliberately.

Step 5: use Step Functions when the business process has ordered state

Fulfillment is different from an independent analytics update. It has explicit sequence and decisions:

flowchart LR
  Start["OrderCreated"] --> Reserve["Reserve inventory"]
  Reserve -->|"Reserved"| Pay["Authorize payment"]
  Reserve -->|"Unavailable"| Reject["Reject or backorder"]
  Pay -->|"Paid"| Ship["Request fulfillment"]
  Pay -->|"Failed"| Release["Release inventory"]
  Ship --> Confirm["Confirm order"]

Use Step Functions when the workflow benefits from visible state, ordered steps, branching, timeouts, retries, Catch paths, and execution history.

The Release inventory branch is a compensating action. It does not reverse time; it performs a new business operation that attempts to offset an earlier successful step.

Standard Workflows fit durable, auditable, potentially long-running coordination. Express Workflows fit high-volume, short-duration execution when their different history and delivery semantics match. Even with workflow guarantees, external side effects and explicitly configured retries should use idempotency.

Step 6: use SNS for notification fanout

After the order is confirmed, publish a notification to an SNS topic when several supported subscribers should receive the same announcement.

flowchart LR
  Confirmed["OrderConfirmed"] --> Topic["SNS topic<br/>announcement system"]
  Topic --> Email["Email channel"]
  Topic --> SMS["SMS channel"]
  Topic --> Queue["Subscriber SQS queue"]

SNS pushes copies to subscribers. SQS stores work for polling consumers. Subscribing SQS queues to SNS combines fanout with durable consumer-specific buffering.

Do not add SNS when EventBridge rules already provide the required application routing. Use it when its push notification or topic fanout model is the actual requirement.

Completed architecture

flowchart LR
  Client["Client"] --> API["Order API"]
  API --> State["Durable order<br/>+ publish intent"]
  State --> Relay["Reliable event relay"]
  Relay --> Bus["EventBridge<br/>content-based routing"]

  Bus --> Workflow["Step Functions<br/>ordered fulfillment"]
  Bus --> AnalyticsQ["SQS<br/>analytics buffer"]
  AnalyticsQ --> Analytics["Lambda<br/>idempotent consumer"]
  Bus --> FraudQ["SQS<br/>fraud buffer"]
  FraudQ --> Fraud["Fraud consumer"]
  Workflow --> Topic["SNS<br/>notification fanout"]
  Topic --> Channels["Email, SMS, or subscriber queues"]

This is not a requirement to use every service in every workflow. It is a map of distinct communication needs.

5. Request Or Data Flow

Learn three lifecycles.

Lifecycle 1: order acceptance

  1. The client sends a checkout request with an idempotency key.
  2. The API validates the request and records the order durably.
  3. The same durable boundary records the intent to publish OrderCreated.
  4. The API returns an accepted order ID.
  5. A relay publishes the committed fact to EventBridge.

The client no longer waits for analytics, email, or the entire fulfillment workflow.

Lifecycle 2: independent queued consumer

  1. An EventBridge rule sends OrderCreated to the analytics SQS queue.
  2. Lambda polls a batch and processes each message.
  3. The handler records an idempotency marker or performs an idempotent update.
  4. Successful messages are removed.
  5. Failed messages become visible for retry; repeated failures move to the consumer DLQ.

Lifecycle 3: coordinated fulfillment failure

  1. Step Functions reserves inventory.
  2. Payment authorization fails after its configured retry policy.
  3. A Catch path records the failure and requests inventory release.
  4. The order state becomes failed or requires review.
  5. Operations can inspect execution history and business records.

The workflow makes the failure path visible. It does not make compensation infallible; compensating actions also need monitoring and retry safety.

6. Security Controls

Limit who can publish and consume

Give producers permission to publish only the expected events to the intended event bus. Give EventBridge the target permissions it needs. Give consumers access only to their queues, data stores, APIs, and secrets.

Use resource policies carefully for cross-account event buses, SNS topics, and SQS queues.

Keep sensitive data out of broad events

Events should normally contain identifiers and the safe context consumers need. Do not broadcast card details, secrets, authentication tokens, or unnecessary personal data through every target.

A useful event might say:

{
  "detail-type": "OrderCreated",
  "detail": {
    "orderId": "order-8472",
    "customerId": "customer-19"
  }
}

Consumers retrieve protected details through authorized systems when necessary.

Encrypt and audit deliberately

Use service encryption and customer-managed KMS keys when key-control requirements justify them. Ensure key policies and IAM permissions allow the publishing and consuming services that need access.

Use CloudTrail for control-plane changes and CloudWatch logs for application and workflow behavior. Redact sensitive payloads before logging.

7. Resilience Controls

The order is not safely accepted until its durable state and publish intent survive the request. An event bus cannot repair an order that was acknowledged only in memory.

Assume messages and events can be delivered more than once. Build idempotent consumers for payments, inventory, notifications, and analytics. Use stable operation identifiers and conditional state transitions.

For SQS consumers:

  • configure visibility timeout around processing behavior;
  • use a redrive policy and consumer DLQ;
  • enable partial batch failure reporting where appropriate; and
  • monitor queue age, not only queue depth.

For EventBridge targets, configure retry and an EventBridge DLQ when delivery failures must be retained. Know which DLQ you are investigating.

For Step Functions, define Retry only for errors that are safe to retry, use Catch for explicit failure paths, and monitor failed or timed-out executions. Compensation should be represented as business state, not assumed to be a perfect rollback.

8. Performance Controls

SQS absorbs bursts but increases asynchronous completion time when arrival rate exceeds processing rate. That is controlled degradation, not free throughput.

Set Lambda concurrency so consumers drain the queue without overwhelming payment providers, databases, or partner APIs. The correct target is sustainable downstream throughput, not the fastest possible empty queue.

Batching reduces polling and invocation overhead but increases the amount of work affected by one invocation. Partial batch responses reduce unnecessary replay of successful records.

Choose queue type from ordering requirements:

  • Standard queues favor high throughput and at-least-once delivery with best-effort ordering.
  • FIFO queues provide FIFO ordering and deduplication features within their documented model, with different throughput and design constraints.

Use Step Functions Standard for durable, auditable coordination and Express for suitable short, high-volume workflows. Choose from execution requirements, not because one name sounds faster.

9. Cost Controls

Event-driven architecture replaces idle servers with several usage meters:

  • EventBridge events and target invocations;
  • SQS API requests and payload transfer;
  • Lambda invocations, duration, and logs;
  • Step Functions state transitions or Express execution resources;
  • SNS deliveries;
  • KMS, data transfer, and downstream service usage.

Filter events before invoking expensive consumers. Give EventBridge rules precise patterns and avoid accidental loops where a target emits an event that matches the same rule repeatedly.

Keep event payloads small. Store large documents in S3 or another suitable data store and send references plus integrity and identity metadata.

Batch SQS messages where it improves efficiency, but account for failure isolation and processing time. Retain logs long enough for operations without storing every payload indefinitely.

Do not introduce EventBridge, SNS, SQS, and Step Functions into the same branch unless each has a distinct requirement. Managed services reduce operations, not architectural cost or cognitive load.

10. Exam Variants

If the question says...Think...Why
"Route events from many producers by content"EventBridgeRules match event fields and send events to targets
"Buffer work while a consumer is unavailable"SQSThe queue retains backlog for polling workers
"Only one worker should process each work item"SQS with competing consumersA queue distributes work rather than broadcasting it to every worker
"Several subscribers need the same notification"SNS topic, often with subscriber SQS queuesSNS fans out; SQS can add durable per-subscriber buffering
"Coordinate ordered steps, retries, branches, and audit history"Step FunctionsA state machine makes workflow control explicit
"A message failed repeatedly and needs isolation"SQS DLQPoison messages leave the main consumer path for investigation
"EventBridge cannot deliver to its target"EventBridge target retry and DLQThis is a routing-delivery failure, not a consumer-processing failure
"Prevent duplicate payment after retry"Idempotent consumer and stable operation IDAt-least-once delivery can repeat a message
"Preserve ordering for a message group"SQS FIFOFIFO is an ordering signal when its constraints fit
"Database commit must not lose event publication"Transactional outbox or reliable change streamIt closes the dual-write gap

11. Common Traps

TrapBetter reasoning
"Every post-order action belongs in the checkout request."Acknowledge after durable acceptance; move independently retryable work off the client path.
"Write the order, then publish, and assume both happen."Preserve publish intent atomically with business state or derive it from a reliable change stream.
"EventBridge is just a durable work queue."EventBridge routes events; SQS provides consumer-controlled backlog and polling.
"One SQS queue fans each message to every service."Consumers on one queue compete; give independent subscribers separate queues or use fanout.
"SNS and SQS are interchangeable."SNS pushes copies to subscribers; SQS stores work for consumers to poll.
"A DLQ fixes bad messages."A DLQ isolates failures; diagnosis, correction, and redrive still require an owner.
"Exactly-once workflow means payment needs no idempotency."Retries and external side effects still require business-level duplicate protection.
"A growing queue means the system is resilient."The work is retained, but increasing message age means the business is falling behind.
"Retry the entire Lambda batch when one record fails."Use partial batch reporting when supported so successful records do not repeat unnecessarily.
"Compensation rolls the distributed system backward."Compensation is a new business action that can also fail and need retries.

Final Mental Model: One-Minute Review

TermExact jobRemember it as
Durable order state and publish intentEnsure an accepted order cannot disappear between storage and event publicationThe signed intake receipt
EventBridgeMatch business events and route them to selected targetsThe mailroom sorting desk
SQSRetain backlog and let workers process at a controlled rateA durable inbox
Lambda consumerPoll and process queued workThe inbox worker
Visibility timeoutTemporarily hide a received message while processing is attemptedThe checked-out work slip
DLQIsolate repeatedly undeliverable or unprocessable work for investigationThe exception tray
Step FunctionsCoordinate ordered steps, branches, retries, and failure pathsThe workflow supervisor
SNSPush one notification to multiple subscribersThe announcement system
IdempotencyMake repeated delivery produce one intended business effectThe duplicate stamp checker
flowchart LR
  API["Order API"] --> State["Durable order<br/>+ publish intent"]
  State -->|"Reliable relay publishes"| Bus["EventBridge<br/>route the fact"]
  Bus --> Queue["SQS<br/>buffer independent work"]
  Queue --> Worker["Lambda<br/>idempotent consumer"]
  Queue -->|"Repeated failure"| DLQ["DLQ<br/>exception isolation"]
  Bus --> Flow["Step Functions<br/>coordinate fulfillment"]
  Flow --> Topic["SNS<br/>fan out notifications"]

The central distinction is route versus buffer versus coordinate versus broadcast. EventBridge chooses destinations, SQS holds work, Lambda performs work, Step Functions manages workflow state, and SNS copies notifications to subscribers. Idempotency makes the whole design safe under repeated delivery.

Use Amazon EventBridge for event buses, patterns, rules, targets, retries, and archives. Use Amazon SQS for queue types, visibility, redrive, and polling. Review AWS Step Functions for Standard and Express workflows, and Amazon SNS for topic fanout and notification endpoints.

Continue to Secure Partner File Ingest On S3 to apply the same buffering, duplicate-handling, and trust-boundary thinking to external file arrivals.

Official AWS references:

Finished reading?

Your reading history is saved in this browser so you can continue later.

Recommended Next

Secure Partner File Ingest On S3AWS Architecture Scenarios17 min read

This applies the foundation mental models to a real architecture decision instead of a service inventory.

Optional exploration

These links add context, but they do not replace the recommended next lesson.

Arcflow Plus is coming — review drills, research breakdowns, more AI. Get one email at launch.