DynamoDB Single-Table Design: The Pattern Everyone Fears (Explained Simply)
Why one table beats five and how to design it without losing your mind.
Hey, it’s Lefteris 👋 I’m the voice behind the weekly newsletter “The Cloud Engineers.”
The first time I saw a DynamoDB single-table design, I closed the tab.
One table holding users, orders, products, and reviews. All mixed together, with cryptic keys like USER#123 and ORDER#456 sitting in the same partition? It looked insane.
It looked insane because I was thinking in relational terms. Once I flipped the mental model, design for access patterns, not entities, it clicked. Here’s the version of this pattern I wish someone had shown me on day one.
Why We Fear It: The Relational Hangover
SQL trains us to normalize. One table per entity, JOINs at query time, let the database figure out the rest. It’s a beautiful model and it’s the exact instinct that makes single-table design feel wrong.
DynamoDB has no JOINs. Every relationship you’d resolve with a JOIN in SQL becomes either multiple round-trips or a modeling decision you make up front. The fear isn’t irrational. It’s a paradigm mismatch. Name it, and it stops being scary.
The One Rule That Changes Everything: Access Patterns First
Here’s the shift: you list your queries before you design your table.
In SQL you model the data and figure out queries later. In DynamoDB you do the opposite. Take a simple e-commerce app and write down what it needs:
Get a user by ID
Get all orders for a user
Get a single order with its line items
Get all products in a category
If you can’t list your access patterns, you’re not ready to model. That discipline is exactly what scares people but it’s also the superpower. You design for what your app actually does, nothing more.
PK, SK, and the Item Collection
Two concepts do most of the work:
Partition Key (PK): determines where an item lives.
Sort Key (SK): orders items within a partition.
The key insight: items sharing the same PK form an “item collection” and are stored together. One query can retrieve the whole collection. That’s how you replace a JOIN, you co-locate related data on purpose.
A Worked Example
Here’s an actual single-table layout for the e-commerce app:
Now watch the access patterns fall out:
Get user 123:
PK = USER#123 AND SK = PROFILEGet all orders for user 123:
PK = USER#123 AND begins_with(SK, "ORDER#")One query, no JOIN, no second round-trip.Get order 555 with its items:
PK = ORDER#555The order and every line item come back together.
The cryptic keys aren’t chaos. They’re a query language you designed on purpose.
GSI Overloading: The Advanced Bit, Made Simple
Some queries go against the grain of your PK/SK like “get all orders with status SHIPPED.” Your main keys can’t answer that.
The solution is a Global Secondary Index built on generic attributes (GSI1PK, GSI1SK) that you reuse across entities. Point GSI1PK at the status, and one index can serve several “sideways” queries. Don’t worry about mastering this on day one, it’s the last 20% you grow into once the basics feel natural.
When NOT to Use Single-Table Design
I draw boundaries, because the pattern isn’t free:
Evolving access patterns. Early-stage products whose queries change weekly, multiple tables are easier to refactor.
Ad-hoc analytics. DynamoDB is for known patterns. Offload analytics to S3 + Athena.
Small apps. Sometimes the operational simplicity of separate tables beats query efficiency.
Team unfamiliarity. The real cost is the learning curve, not the technology.
The Cost & Performance Payoff
This is where it earns its place in a scalable, cost-efficient stack:
Fewer tables to provision, monitor, and pay for.
One query instead of N round-trips means lower latency and fewer read capacity units consumed.
Item collections keep related data on one partition, giving you predictable single-digit-millisecond reads at scale.
Where to Start
Don’t boil the ocean. Pick one feature in your app. List its queries. Model just that as a single table. Once you’ve watched a JOIN disappear into a single Query call, the fear is gone for good, and you’ll never look at those USER#123 keys the same way again.


