Skip to content

DynamoDB Single-Table vs Multi-Table Design: The Real Difference

If you’re coming from SQL, the instinct is to put Users in a users table, Orders in an orders table, and Products in a products table. DynamoDB doesn’t stop you. It just rewards a different choice.

Single-table design puts all your entity types in one DynamoDB table with carefully overloaded partition and sort keys. Multi-table design gives each entity type its own table. Both work. The difference is what you’re optimizing for.

The short version

Use single-table when your access patterns require fetching multiple entity types together (a customer and their orders, a team and its members, a post and its comments). The query that returns both in one call is only possible when they share a table.

Use multi-table when your entity types have no access patterns that span them, your team is new to DynamoDB and needs the simpler mental model, or you’re building a narrow service that owns one entity type and queries it in only one or two ways.

The single-table advantage isn’t ideology. It’s the fetch. You cannot Query across two DynamoDB tables. If you need data from two entity types and they’re in separate tables, that’s two network calls. Every time.

Comparison

Single-tableMulti-table
Fetch multiple entity typesOne QueryOne call per table
DynamoDB transactionsSame table (up to 100 items)Cross-table transactions (same limit, more complex)
Schema complexityHigher (key overloading required)Lower (each table has one entity shape)
IAM policyOne table ARNOne ARN per table
GSI designShared GSIs, requires disciplinePer-table GSIs, simpler to reason about
CloudWatch metricsAll entities togetherPer-entity visibility
AWS table limitOne table (no issue)2,500 per account/region
Team learning curveSteeperGentler
Querying ad-hoc (PartiQL, console)Harder (all entities mixed)Easier (one entity per table)

What multi-table looks like in practice

dynamodb-table: users
dynamodb-table: orders
dynamodb-table: products
dynamodb-table: inventory

Each table has a simple key structure. users table: pk = userId. orders table: pk = orderId, sk = customerId or a GSI. Queries within one entity type are straightforward.

The problem emerges at the join. In SQL: SELECT * FROM orders JOIN users ON orders.customer_id = users.id. In DynamoDB multi-table: two requests (GetItem(users, userId) plus Query(orders, customerId)) merged in application code.

For a customer profile page showing order history, that’s acceptable. For a notifications page showing 50 items across 6 entity types, that’s 6 parallel DynamoDB calls, 6 sets of pagination cursors, and client-side merging. It’s workable but you’re fighting the database instead of working with it.

What single-table looks like in practice

All entity types share one table. The partition key is overloaded with entity type prefixes:

PK: CUSTOMER#<customerId>,  SK: #METADATA        → customer record
PK: CUSTOMER#<customerId>,  SK: ORDER#<orderId>  → order (customer view)
PK: ORDER#<orderId>,        SK: #METADATA        → order (direct lookup)
PK: ORDER#<orderId>,        SK: ITEM#<productId> → order item

The CUSTOMER#<id> partition contains both the customer record and their orders. One Query returns all of it:

// ElectroDB collection: fetch customer + all their orders
const { data } = await ShopService.collections
  .customerWithOrders({ customerId: "cust_01" })
  .go()
// data.customers → customer record
// data.orders    → Order[] records
// One DynamoDB Query

That’s the concrete difference. Not philosophy. One network round trip vs two.

The real tradeoff: operational simplicity vs query efficiency

Multi-table wins on operational simplicity. Each table is self-contained. You can add a GSI to orders without touching users. You can give the team that owns users a narrow IAM policy scoped to the users table. CloudWatch shows you orders throughput separately from users throughput.

Single-table wins on access pattern coverage. Once data is in the same partition, you can get it in one query. Access patterns you didn’t anticipate when you first designed the schema can often be served with a new GSI on the shared table, no data migration needed, because all the data is already together.

The tradeoff isn’t theoretical. In production applications, access patterns accumulate. New features need new data combinations. A schema that forced two round trips at launch is forcing six at maturity.

When multi-table is actually the right call

Your entity types have no relationship access patterns. A system that processes files independently (ingests a file, stores metadata, marks it processed) has no need to fetch multiple entity types together. One table per entity type keeps each table comprehensible.

Separate service ownership. In a microservices architecture where the orders service owns the orders table and the users service owns the users table, keeping them separate is correct. The services cross-communicate via API, not shared database. Single-table design within each service (if needed) is still possible.

Narrow domain, simple queries. A feature flag service with FeatureFlag and FlagOverride entities, where every access pattern touches one entity at a time, doesn’t need single-table. The shared table adds complexity with no benefit.

Team is new to DynamoDB. Single-table design requires understanding composite key templates, GSI overloading, and entity type disambiguation in queries. If your team is still building DynamoDB intuition, multi-table is a valid starting point. You can migrate toward single-table as access patterns get more complex. Migrations are painful though, so it’s worth reading the common mistakes before locking in either direction.

When single-table is clearly right

Any access pattern that fetches multiple entity types together. Customer + orders. Post + comments + author. Team + members + recent activity. These are single-query operations in single-table design and two-or-more calls in multi-table.

Complex query combinations. If you need to support queries by owner, by status, by date range, and by category across the same data set, GSI overloading in a single table is cleaner than managing separate GSIs across multiple tables that all need to be queried and merged.

Cross-entity search or listing. An admin dashboard that shows all items of all types sorted by createdAt. This is a single GSI query in single-table and a multi-table merge with complex pagination otherwise.

DynamoDB Streams + event processing. One stream, one Lambda consumer, all entity events. In multi-table this requires one stream consumer per table.

The access pattern test

Before deciding, write out every access pattern your application needs. Not “get user”, but “get user profile with their three most recent orders” or “list all active subscriptions for an organization sorted by creation date”. Then ask: does each access pattern touch only one entity type, or multiple?

If most of your access patterns are single-entity, multi-table is defensible. If most require multiple entity types, single-table is the correct choice.

The E-Commerce Orders pattern shows a concrete single-table schema with 8 access patterns across 3 entity types. The SaaS Multi-Tenant pattern handles 10 access patterns across 4 entity types in one table. Both would require 3–4 round trips per complex query in a multi-table design.

Decision framework

SituationUse
Access patterns cross entity types (customer + orders, team + members)Single-table
Services own their data independently (microservices)Multi-table (per service)
Access patterns are single-entity onlyEither (multi-table is simpler)
DynamoDB Streams processing across entity typesSingle-table
Team is unfamiliar with DynamoDBMulti-table (start here, evolve)
Large admin queries across all entity typesSingle-table
Regulatory isolation between entity typesMulti-table
Performance-sensitive, high read throughputSingle-table (fewer round trips)

The bottom line

Multi-table DynamoDB looks like a relational database schema without the JOINs. That’s not a compliment. You lose the primary advantage of relational design (JOINs) without gaining the primary advantage of DynamoDB (access-pattern-first co-location of related data).

Single-table design requires more upfront thinking about access patterns and key structure. It pays for itself the first time you fetch a customer and their orders with one query instead of two.

If you’re building a new application and you know access patterns will span multiple entity types, start single-table. The access pattern design work is required regardless; the key structure just makes it explicit.

If you’re evaluating whether your existing multi-table setup needs to change, the question is whether the extra round trips are actually costing you. If your access patterns are simple and single-entity, they might not be.


The question of single-table vs multi-table is different from table-per-tenant, which is about whether each customer of your SaaS gets their own table. That has its own tradeoffs covered in single-table vs table-per-tenant. If you want to see what good single-table design looks like end-to-end, the E-Commerce Orders pattern and SaaS Multi-Tenant pattern are good starting points.

Tejovanth N

Tejovanth builds on DynamoDB in production: rasika.life, rekha.app, rrmstays. All single-table with ElectroDB.

LinkedIn codeculturecob.com

FAQ

Can you query across two DynamoDB tables in one request?

No. A Query or GetItem hits one table, never two. If your entities live in separate tables, fetching both is two network calls you stitch together in code. Put them in the same table and one request can return both.

Is single-table design always better than multi-table in DynamoDB?

No. Single-table wins when an access pattern needs more than one entity type at once, since you get them in a single Query. Multi-table makes more sense if your entities never need fetching together, if separate services own their data, or if the team is still new to DynamoDB.

Can I migrate from multi-table to single-table design later?

Yes, but it hurts. You're rewriting key structures, backfilling every item into the new overloaded-key format, and reworking query code to match. Much cheaper to map your access patterns first and pick the right model before there's production data to move.

How many tables can a DynamoDB account have?

By default AWS gives you 2,500 tables per account per region, and you can request more. So the table count is rarely the real reason to consolidate. It's the extra round trips and operational overhead that push teams toward one table as the system grows.

Related