Decouple an Order Processing System
Build a fan-out architecture with SQS, SNS, and Dead Letter Queues.
Introduction
30 Second Summary
When you order something online, dozens of things happen at once. Payment processes, a shipping label prints, and analytics update. If one of those downstream systems crashes, should it take down the entire checkout?
In this project, you will build a mini order processing system using Amazon SQS and Amazon SNS that decouples downstream services so they operate independently and fail gracefully.
What You'll Build
You'll publish one order message and watch it appear in three separate queues at once, then break message ordering, fix it with FIFO, and catch failed messages in a Dead Letter Queue.
By the end of this project, you'll have:
- A working fan-out architecture where you publish a single order notification and watch it arrive simultaneously in three independent queues representing shipping, billing, and analytics.
- A FIFO queue that guarantees numbered order messages arrive in exact 1-2-3-4-5 sequence, compared side-by-side with a Standard queue where order is not guaranteed.
- A Dead Letter Queue that automatically captures failed messages, proving how resilient decoupled systems handle poison-pill scenarios without blocking the main flow.
- Secret Mission: Route high-priority orders to a VIP queue using SNS filter policies while regular orders flow to the standard processing queue.
Are there any prerequisites?
You need an AWS account with console access. This project is entirely console-based, so no CLI installation or coding experience is required.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Your Queues and Topic
Every decoupled system starts with the plumbing: something to hold messages and something to route them. Before you can see fan-out, ordering, or failure handling in action, you need the foundational messaging infrastructure in place.
In this step, you will create your first Amazon SQS queue, send a test message through it, and set up the Amazon SNS topic that will power the fan-out pattern in the next step.
In this step, get ready to:
- Create a Standard SQS queue with exam-testable default settings.
- Send and receive a test message to prove the producer-consumer pattern works.
- Create an SNS topic that will broadcast messages to multiple queues.
Create the Standard queue
A Standard queue is the default SQS queue type. It offers unlimited throughput and best-effort ordering.
- In the AWS Management Console search bar, type SQS and select Simple Queue Service.
- Click Create queue.
- For Type, confirm that Standard is selected (it is the default).
- In the Name field, enter orders-processing.
- Leave all configuration settings at their defaults.
- Scroll to the bottom and click Create queue.
The console displays your new queue's Details page. You should see orders-processing listed with a Type of Standard.
What are the default settings?
The defaults you just accepted are exam-testable values on the SAA-C03. Memorise these three:
- Visibility timeout: 30 seconds (how long a message is hidden from other consumers after being read).
- Message retention period: 4 days (how long undelivered messages stay in the queue before being deleted).
- Maximum message size: 256 KB (the largest payload a single message can carry).
Don't see the queue?
SQS is a distributed system. There may be a brief delay before the new queue appears in the Queues list. Refresh the page after a few seconds.
help me find my SQS queue in the console
Send and receive a test message
The basic SQS pattern is producer-consumer: one service sends a message, another reads it. You will prove this works by acting as both sides.
- On the orders-processing queue details page, click Send and receive messages in the upper right.
- In the Message body field, paste the following JSON:
{"orderId": "001", "item": "Laptop", "status": "new"}
- Click Send message.
You should see a green success banner confirming the message was sent.
Now act as the consumer. Before you poll, what do you expect to see?
- Scroll down to the Receive messages section on the same page.
- Click Poll for messages.
Your test message appears in the messages table. Click on it to expand the body and confirm you see the JSON you sent.
What just happened?
You proved the core SQS pattern: a producer pushed a message into the queue, and a consumer pulled it out. The message persisted in the queue until you polled for it. This is the pull-based model that distinguishes SQS from push-based services like SNS.
Message not showing up?
If no messages appear after polling, check that you are on the Send and receive messages page for the correct queue (orders-processing). Also make sure you clicked Send message before polling.
If the message appeared and then disappeared, it may have passed its 30-second visibility timeout and become available again. Click Poll for messages once more.
help me troubleshoot my missing SQS message
Create the SNS topic
An SNS topic is a broadcast channel. When you publish a message to a topic, every subscriber receives a copy. This is how you will fan out a single order notification to multiple queues in the next step.
- In the AWS Console search bar, type SNS and select Simple Notification Service.
- In the left navigation pane, click Topics.
- Click Create topic.
- For Type, select Standard.
- In the Name field, enter orders-topic.
- Scroll to the bottom and click Create topic.
The console displays your new topic's details page. You should see the Topic ARN in the Details section.
- Copy the ARN value (it looks like arn:aws:sns:us-east-1:123456789012:orders-topic). You will need it in the next step when subscribing queues to this topic.
What is an ARN?
An ARN (Amazon Resource Name) is a unique identifier for any AWS resource. It includes the service, region, account ID, and resource name. You will use this ARN to tell SNS which SQS queues should receive messages.
Why SNS plus SQS instead of just SQS?
SQS alone works for point-to-point messaging: one producer, one consumer. But when multiple downstream services need the same message, you would have to publish it to each queue individually.
SNS solves this with the fan-out pattern. You publish once to the topic, and SNS pushes a copy to every subscribed queue automatically. The publisher never needs to know how many consumers exist.
Your messaging infrastructure is ready. Next up, you will wire these pieces together and watch a single published message fan out to three independent queues.
Build the Fan-Out Pattern
In the previous step, you proved that a single queue can receive and hold a message. That covers one consumer. But real-world order systems need multiple services to react to the same event simultaneously.
Imagine a customer places an order. Shipping needs to know, billing needs to know, and analytics needs to know. Without fan-out, your publisher has to send the same message three separate times and track which services received it. SNS solves this by acting as a broadcast layer: one publish, and every subscribed queue gets a copy automatically.
In this step, get ready to:
- Create two additional SQS queues to represent the shipping and billing services.
- Subscribe all three queues to your SNS topic with proper permissions.
- Publish one message and confirm it arrives in all three queues simultaneously.
Create the shipping and billing queues
Your orders-processing queue represents one downstream service. You need two more to demonstrate that fan-out delivers a single message to multiple independent consumers.
- Return to the Amazon SQS console.
- Click Create queue.
- For Type, confirm Standard is selected.
- For Name, enter orders-shipping.
- Leave all other settings as defaults and click Create queue.
You should see the queue details page for orders-shipping.
- Repeat the same process to create a second queue named orders-billing.
- Click Queues in the left navigation pane to see all your queues.
You should now see three queues in the list: orders-processing, orders-shipping, and orders-billing.
Don't see all three queues?
Make sure you're in the same AWS Region where you created orders-processing. Check the region selector in the top-right corner of the console.
help me find my SQS queues in the console
Subscribe all three queues to the SNS topic
With your queues ready, you need to wire them up to orders-topic so that a single publish reaches all three. Each queue gets its own independent subscription.
- Navigate to the Amazon SNS console.
- In the left navigation pane, click Topics.
- Click on orders-topic to open it.
- Click Create subscription.
- For Protocol, select Amazon SQS.
- For Endpoint, select or paste the ARN of orders-processing.
- Check the box for Enable raw message delivery.
- Click Create subscription.
What is raw message delivery?
Without raw message delivery, SNS wraps your message body inside a JSON envelope that includes metadata like timestamps and signature info. With it enabled, the message body arrives in SQS exactly as you published it.
This simplifies your consumer code because it does not need to parse SNS metadata to get the actual payload.
The subscription should show as Confirmed almost immediately because you own both the topic and the queue in the same account.
- Return to orders-topic and repeat the subscription process for orders-shipping (same steps: protocol Amazon SQS, endpoint is the orders-shipping ARN, enable raw message delivery).
- Repeat once more for orders-billing.
On the orders-topic page, scroll to the Subscriptions section. You should see three subscriptions, all with status Confirmed.
Why three separate subscriptions?
Each subscription is an independent link between the topic and one queue. The queues do not know about each other. If you remove one subscription, the other two keep working. This is the decoupling guarantee that makes fan-out powerful on the exam and in production.
Configure access policies for SNS delivery
For SNS to deliver messages into your SQS queues, each queue's access policy must explicitly allow it. When you created the subscriptions, the console may have prompted you to automatically add this permission. Let's verify it is in place.
- Return to the Amazon SQS console and click on orders-processing.
- Select the Access policy tab.
- Look for a statement that allows the sns.amazonaws.com service to perform sqs:SendMessage with an ArnEquals condition on aws:SourceArn matching your topic ARN.
If the policy statement is already there, you are good to go. If it is missing, you need to add it manually.
- Click Edit on the Access policy tab.
- Add the following statement inside the Statement array of the existing JSON policy:
{
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:REGION:ACCOUNT-ID:orders-processing",
"Condition": {
"ArnEquals": {
"aws:SourceArn": "arn:aws:sns:REGION:ACCOUNT-ID:orders-topic"
}
}
}
What does this policy do?
This statement says: only the sns.amazonaws.com service is allowed to call sqs:SendMessage on this queue, and only when the request comes from your specific SNS topic ARN.
The aws:SourceArn condition prevents other topics (or other AWS services) from sneaking messages into your queue. This is a best practice for the SAA-C03 exam and real-world deployments.
- Replace REGION and ACCOUNT-ID with your actual values (visible in the queue ARN at the top of the page).
- Click Save.
- Repeat for orders-shipping and orders-billing (update the Resource ARN to match each queue's ARN).
Faster approach
If the console already added the SNS permission automatically during subscription creation, you do not need to manually paste the JSON. Just confirm the statement exists in each queue's access policy tab before moving on.
Subscription showing Pending Confirmation?
If a subscription is stuck in Pending Confirmation status, the access policy was likely missing when the subscription was created. Add the policy statement above to the queue, then delete and recreate the subscription.
help me fix my SNS subscription that is stuck in pending confirmation status
Publish a message and verify fan-out
Everything is wired up. One topic, three subscriptions, three queues with the correct permissions. Time to see the fan-out pattern in action.
- In the SNS console, open orders-topic.
- Click Publish message.
- Leave the Subject field empty.
- In the Message body field, paste the following:
{"orderId": "002", "item": "Monitor", "status": "new"}
Before you click publish, think: how many queues do you expect this single message to appear in?
- Click Publish message.
You should see a green success banner confirming the message was published.
- Navigate back to the SQS console.
- Click on orders-processing.
- Click Send and receive messages.
- Click Poll for messages.
You should see your order message appear. The body reads exactly {"orderId": "002", "item": "Monitor", "status": "new"} because raw message delivery strips the SNS metadata wrapper.
- Go back to Queues and repeat the poll process for orders-shipping.
- Repeat for orders-billing.
All three queues have the identical message. One publish, three independent consumers. This is the fan-out pattern.
Why does this matter for the SAA-C03 exam?
The SNS-to-SQS fan-out is the single most tested decoupling pattern on the exam. When a question says "multiple services need to independently process the same event," the answer is almost always an SNS topic with SQS queue subscriptions.
The key insight: the publisher does not need to know how many consumers exist. You can add or remove subscriptions without changing the publishing code.
One or more queues show 0 messages?
Check that the subscription for that queue shows Confirmed status in the SNS console. If it shows Pending Confirmation, the queue's access policy is missing the SNS permission.
Also verify you clicked Poll for messages in the SQS console. Messages exist in the queue but are not displayed until you explicitly poll.
help me troubleshoot why my SQS queue did not receive the SNS fan-out message
Your fan-out architecture is working. Next up, you will discover why message ordering is not guaranteed in Standard queues and fix it with a FIFO queue.
Discover Ordering Problems and Fix with FIFO
Your fan-out architecture is working. One published message reaches three independent queues simultaneously.
But what happens when the order of messages matters? Imagine an "order shipped" notification arriving before "order placed." In this step, you will send numbered messages to a Standard queue, observe its best-effort ordering, and then build a FIFO queue that guarantees strict sequencing.
In this step, get ready to:
- Send numbered messages to a Standard queue and observe best-effort ordering.
- Create a FIFO queue with content-based deduplication.
- Verify strict message ordering in the FIFO queue.
Send numbered messages to the Standard queue
Your orders-processing queue is a Standard queue. Standard queues offer best-effort ordering, meaning messages can arrive out of sequence.
- Return to the SQS console and select the orders-processing queue from the list.
- Click Send and receive messages.
- In the Message body field, type Order 1 and click Send message.
- Repeat this quickly for Order 2, Order 3, Order 4, and Order 5. Send each one as fast as you can.
Before you poll, what order do you expect these five messages to arrive in?
- Scroll down to the Receive messages section and click Poll for messages.
- Look at the order the messages appear in the list.
With a Standard queue, there is no ordering guarantee. Your messages may have arrived in 1-2-3-4-5 order, or they may be shuffled. The key point is that the system makes no promise about sequence.
Why is the order not guaranteed?
Standard queues are optimized for maximum throughput. Messages are distributed across multiple servers, and whichever server responds first delivers its message first. This is called best-effort ordering.
For many use cases (like sending emails or resizing images) order does not matter. But when processing depends on sequence (order placed, then order shipped, then order delivered), you need something stronger.
Create a FIFO queue
A FIFO queue guarantees that messages are delivered in the exact order they were sent. It also provides exactly-once processing within a 5-minute deduplication window.
- In the SQS console left navigation, click Queues.
- Click Create queue.
- For Type, select FIFO.
- For Name, enter orders-priority.fifo.
Why does the name end in .fifo?
The .fifo suffix is mandatory for all FIFO queues. SQS uses this suffix to identify the queue type. If you omit it, the queue creation will fail.
- Under Configuration, check the box for Content-based deduplication.
- Set Deduplication scope to Queue.
- Set FIFO throughput limit to Per queue.
- Leave all other settings at their defaults and click Create queue.
You should see a success banner confirming your orders-priority.fifo queue was created.
What is content-based deduplication?
When enabled, SQS automatically generates a deduplication ID by hashing the message body. If you accidentally send the same message twice within 5 minutes, SQS delivers it only once. This prevents duplicate processing without requiring you to manually track message IDs.
FIFO throughput is limited to 300 messages per second without batching (compared to unlimited for Standard queues). This tradeoff is what buys you the ordering guarantee.
Queue creation failed?
Make sure the queue name ends with exactly .fifo (lowercase, no spaces). Also confirm you selected FIFO as the queue type at the top of the form before entering the name.
Help me troubleshoot my FIFO queue creation
Verify strict ordering in the FIFO queue
Now you will send the same five numbered messages to your FIFO queue and confirm they arrive in the exact order you sent them.
- Select the orders-priority.fifo queue from the queue list.
- Click Send and receive messages.
- In the Message body field, type Order 1.
- In the Message group ID field, enter priority-orders.
- Click Send message.
- Repeat for Order 2, Order 3, Order 4, and Order 5. Use the same Message group ID priority-orders for every message.
What is the Message group ID?
The Message group ID tells SQS which messages belong to the same ordered sequence. All messages sharing a group ID are processed in strict order, one at a time. Messages in different groups can be processed in parallel.
By using a single group ID for all five messages, you are telling SQS these messages must stay in the exact sequence you sent them.
Before you poll, do you expect these messages to arrive in strict 1-2-3-4-5 order, or could they be shuffled like the Standard queue?
- Scroll down to the Receive messages section and click Poll for messages.
Your messages arrive in exact 1-2-3-4-5 order. This is the FIFO guarantee in action. Every message within the same group ID is delivered in the precise sequence it was sent.
When should you choose FIFO on the exam?
If an SAA-C03 scenario says "messages must be processed in order" or "no duplicate processing," the answer is a FIFO queue. Standard queues trade ordering guarantees for unlimited throughput.
FIFO queues handle 300 messages per second without batching (3,000 with batching). Standard queues have no throughput limit. Pick FIFO only when ordering or deduplication is a requirement.
Messages not appearing in order?
Confirm you entered the same Message group ID (priority-orders) for every message. Messages with different group IDs can arrive out of order relative to each other. Also make sure you selected the FIFO queue (orders-priority.fifo) and not one of your Standard queues.
Help me figure out why my FIFO messages are out of order
You have now seen the ordering gap in Standard queues and proven that FIFO queues close it. Next up, you will configure a Dead Letter Queue to handle messages that fail to process.
Configure Dead Letter Queues for Failed Messages
Your fan-out and FIFO queues are working. But what happens when a message fails to process?
In the real world, messages sometimes fail. Without a safety net, these "poison pill" messages get retried forever or block your queue entirely. A Dead Letter Queue isolates failures so you can debug them without blocking the main flow. On the SAA-C03 exam, DLQs appear in nearly every resilience scenario.
In this step, get ready to:
- Create a Dead Letter Queue to capture failed messages.
- Configure a redrive policy on your source queue.
- Simulate a failed consumer and watch a message move to the DLQ automatically.
Create the Dead Letter Queue
A Dead Letter Queue is just a regular SQS queue that you designate as the destination for failed messages. You create it first, then point your source queue at it.
- Return to the SQS console and click Create queue.
- Select Standard as the queue type.
- Enter orders-dlq as the queue name.
- Leave all other settings at their defaults.
- Click Create queue.
You should see the orders-dlq details page confirming the queue was created successfully.
Why must the DLQ be the same type as the source queue?
A Standard queue can only use a Standard DLQ. A FIFO queue can only use a FIFO DLQ. The DLQ must also live in the same AWS account and region as the source queue.
These are hard rules that appear frequently on the SAA-C03 exam.
Don't see the Create queue button?
Make sure you are in the Queues list view. Click Queues in the left navigation pane to return to the list, then look for the Create queue button in the upper right.
Help me find the Create queue button in SQS
Link the DLQ to your source queue
With the DLQ created, you need to tell orders-processing to send failed messages there. You do this by configuring a redrive policy with a maxReceiveCount value.
- In the left navigation, click Queues to return to the queue list.
- Select orders-processing from the list.
- Click Edit.
- Scroll down to the Dead-letter queue section and toggle it to Enabled.
- Under Dead-letter queue settings, select the ARN for orders-dlq from the dropdown.
- Set Maximum receives to 2.
- Click Save.
The console returns to the queue details page. You should see the Dead-letter queue tab now showing the DLQ ARN and a Maximum receives value of 2.
What does Maximum receives mean?
The Maximum receives value (maxReceiveCount) defines how many times a consumer can receive a message before SQS moves it to the DLQ. Setting it to 2 means that after a message is received 2 times without being deleted, the next receive attempt moves it to the DLQ.
In production, you would set this higher (e.g. 5 or 10) to allow retries. For this demo we keep it low so you can trigger the DLQ quickly.
Test the DLQ by simulating a failed consumer
Now for the proof. You will send a message, poll it without deleting it, wait for the visibility timeout to expire, and repeat. After the receive count exceeds your maxReceiveCount of 2, the message should vanish from the source queue and appear in the DLQ.
- Select orders-processing from the queue list.
- Click Send and receive messages.
- In the Message body field, enter: {"orderId": "099", "item": "Broken Widget", "status": "poison-pill"}.
- Click Send message.
Now simulate a consumer that receives but never deletes the message.
- Scroll down and click Poll for messages. You should see your message appear in the Messages section. This is receive 1.
- Do NOT delete the message. Wait 30 seconds for the visibility timeout to expire.
- Click Poll for messages again. The same message reappears. This is receive 2.
- Do NOT delete the message. Wait another 30 seconds.
Watching the receive count
Each time a message appears in the Messages section after polling, the Receive count column increments. Watch it go from 1 to 2 across your two polls.
Before you poll a third time, what do you think will happen? Will the message appear again, or will it be gone?
- Click Poll for messages one more time.
The message is no longer in orders-processing. SQS moved it to the DLQ because the receive count exceeded your maxReceiveCount of 2.
- Navigate back to Queues in the left navigation.
- Select orders-dlq from the list.
- Click Send and receive messages.
- Click Poll for messages.
Your poison-pill message is sitting in the DLQ. This is exactly how DLQs protect your system from unprocessable messages in production.
Why this matters on the SAA-C03 exam
When an exam question says "messages that fail to process should be isolated for debugging," the answer is a Dead Letter Queue. Key facts to remember: the DLQ must be the same type as the source queue, it must be in the same account and region, and the maxReceiveCount range is 1 to 1000.
Message didn't move to the DLQ?
Make sure you waited the full 30 seconds between polls. The visibility timeout must expire before the message becomes available again for the next receive. If you poll too quickly, the message is still "in flight" and does not increment the receive count.
Also confirm that Maximum receives is set to 2 on orders-processing (not on the DLQ itself). The redrive policy is configured on the source queue, not the DLQ.
Help me troubleshoot my DLQ redrive policy
You now have a resilient messaging system. Your DLQ catches failed messages automatically, keeping the main queue healthy. Next up, you will tackle the Secret Mission and route messages based on attributes using SNS filter policies.
Secret mission
Add SNS Message Filtering
Right now, every subscriber to your SNS topic receives every message. On the SAA-C03 exam, you will see scenarios where different message types need different routing. Can you use SNS subscription filter policies to route high-priority orders to a VIP queue while regular orders go to the standard processing queue?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely within the AWS free tier, so there are no ongoing costs when your queues and topic are idle.
Resources you used:
- SQS Standard queues: orders-processing, orders-shipping, orders-billing, orders-dlq, orders-vip.
- SQS FIFO queue: orders-priority.fifo.
- SNS Standard topic: orders-topic (with 4 SQS subscriptions).
Keep everything running
No action needed. Choose this if you are still actively experimenting with your queues and topic.
- SQS queues and SNS topics have no cost when idle. You are only charged when messages flow through them.
- Your usage in this project is well within the free tier (1 million SQS requests per month and 1 million SNS publishes per month are included at no charge).
Pause - I'll come back to this later
Since SQS and SNS are serverless and cost nothing when idle, pausing is the same as keeping them. No running processes need to be stopped.
- Your queues and topic remain available whenever you return.
- Any messages still in a queue will be automatically deleted after the retention period expires (4 days by default).
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow these steps to delete everything.
- Navigate to the Amazon SNS console. In the left navigation pane, choose Topics.
- Select orders-topic, then choose Delete.
- In the Delete topic dialog box, enter delete me and choose Delete. This removes the topic and all its subscriptions automatically.
- Navigate to the Amazon SQS console. In the left navigation pane, choose Queues.
- Select all six queues: orders-processing, orders-shipping, orders-billing, orders-dlq, orders-vip, and orders-priority.fifo.
- Choose Delete. In the Delete queue dialog box, enter delete to confirm, then choose Delete.
- Repeat for each queue if the console requires individual confirmation.
That's a Wrap!
That's a Wrap!
Nice work! You just built a fully decoupled order processing system using Amazon SQS and SNS, covering the three most-tested messaging patterns on the SAA-C03 exam.
You've learned how to:
- Build a fan-out architecture where one SNS topic delivers messages to three independent SQS queues simultaneously, letting each downstream service process orders independently.
- Compare Standard vs FIFO queues side-by-side, experiencing firsthand how FIFO guarantees strict message ordering while Standard queues deliver in best-effort order.
- Configure a Dead Letter Queue with a redrive policy to automatically isolate failed messages after maxReceiveCount is exceeded, protecting your main queue from poison-pill messages.
- Secret Mission: Implemented SNS subscription filter policies to route high-priority orders to a VIP queue based on message attributes, without changing your publisher code.
Ready to quiz yourself?