
September 09, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Amazon DynamoDB data modeling for developers is not SQL with fewer joins. You design around how the application reads and writes data, then choose partition keys, sort keys, and indexes to match those paths. Teams coming from PostgreSQL for Laravel developers often hit throttling, hot partitions, or costly scans because they normalized first. This guide walks through the mental model, concrete item shapes, and the trade-offs you will face on real projects—including when DynamoDB is the wrong tool.
GetItem or Query—never a table scan. Denormalize aggressively; one item can hold an order header and its line items.How is Amazon DynamoDB data modeling different from relational design?
In MySQL or PostgreSQL, you start with entities and foreign keys. DynamoDB flips that order. You start with questions like "fetch all orders for user X" or "list inventory by warehouse." Each question becomes an access pattern with a known key structure.
DynamoDB is a key-value store with optional sort keys inside each partition. There are no joins at query time. If two record types relate, you either duplicate data across items or run multiple queries and assemble results in application code.
That shift affects cost and latency directly. A well-modeled table serves reads in single-digit milliseconds at predictable RCU/WCU. A poorly modeled one triggers Scan operations that burn capacity and time out under load.
On a production Laravel application I maintained, the team kept PostgreSQL as the system of record. DynamoDB backed high-volume session tokens and webhook deduplication. That split worked because each store matched its access pattern. For greenfield API development, document those patterns before you pick the database.
The access-pattern worksheet
Before writing CloudFormation or CDK, list patterns in a table. Example for an eCommerce order service:
| Pattern | Operation | Key shape | Frequency |
|---|---|---|---|
| Get order by ID | GetItem | PK=ORDER#id | High |
| List user orders | Query | PK=USER#id, SK begins ORDER# | High |
| Orders by status | Query on GSI | GSI1PK=STATUS#pending | Medium |
| Inventory by SKU | GetItem | PK=SKU#id | High |
If a pattern cannot map to a key condition, redesign the item or add a GSI. Scans are the escape hatch—and they should stay rare.
What are partition keys and sort keys in DynamoDB?
The partition key (PK) determines which physical partition stores your item. DynamoDB hashes the PK value and routes the request. All items sharing one PK live in the same partition and sort by the sort key (SK).
Choose PK values that spread load evenly. A PK of STATUS#pending for every open order creates a hot partition. Prefer high-cardinality prefixes like USER#<uuid> or ORDER#<uuid>.
Sort keys enable range queries within a partition. SK=ORDER#2026-09-08#ord-991 lets you fetch one order or all orders for a user between two dates with BETWEEN.
Overloaded keys and entity type prefixes
Single-table design stores multiple entity types in one table. Prefix PK and SK values so items are distinguishable:
// Order header item
{
"PK": "USER#7f3a",
"SK": "ORDER#2026-09-08T14:30:00Z#ord-991",
"entityType": "Order",
"status": "pending",
"totalNpr": 4500,
"gsi1pk": "STATUS#pending",
"gsi1sk": "2026-09-08T14:30:00Z"
}
// Line item nested in same partition
{
"PK": "ORDER#ord-991",
"SK": "LINE#1",
"entityType": "LineItem",
"sku": "SKU-001",
"qty": 2
} The entityType attribute helps unmarshaling code route items to the right struct. Use a JSON formatter during design reviews so the team agrees on shapes before deployment.
How do you model one-to-many relationships in DynamoDB?
Relational databases use join tables. DynamoDB offers three practical patterns.
- Adjacency list: Parent and children share a PK; children differ by SK. One
Queryreturns the full tree. - Materialized aggregation: Store child collections as a list or map inside the parent item—fine for small, bounded sets.
- Duplicate items (index overloading): Write the same logical record twice with different PK/SK pairs so two access patterns each get a direct query.
For a booking platform like those in my Adventure Third Pole Trek portfolio work, "list bookings by customer" and "list bookings by date" both need fast paths. Duplicate items with GSI keys cost extra writes but eliminate scan-and-filter logic.
Write path with duplicate items
import { DynamoDBClient, TransactWriteItemsCommand } from "@aws-sdk/client-dynamodb";
const client = new DynamoDBClient({ region: "ap-south-1" });
await client.send(new TransactWriteItemsCommand({
TransactItems: [
{
Put: {
TableName: "Bookings",
Item: {
PK: { S: "CUSTOMER#cust-42" },
SK: { S: "BOOKING#2026-10-01#bk-77" },
gsi1pk: { S: "DATE#2026-10-01" },
gsi1sk: { S: "BOOKING#bk-77" },
status: { S: "confirmed" }
}
}
},
{
Put: {
TableName: "Bookings",
Item: {
PK: { S: "BOOKING#bk-77" },
SK: { S: "METADATA" },
customerId: { S: "cust-42" },
tripDate: { S: "2026-10-01" }
}
}
}
]
})); TransactWriteItems keeps both copies consistent. If one write fails, neither lands—a pattern worth copying from relational transaction thinking.
When should you use GSIs versus LSIs in DynamoDB?
Global Secondary Indexes (GSIs) have their own partition key and optional sort key. They can differ entirely from the base table. Local Secondary Indexes (LSIs) share the base table PK but use an alternate SK—only creatable at table creation time.
In practice, GSIs carry most alternate-access-pattern load. You pay per-GSI write amplification: each base-table write may trigger a GSI write. Budget for that in WCU planning.
| Feature | GSI | LSI |
|---|---|---|
| Key schema | Independent PK + optional SK | Same PK, alternate SK |
| Create timing | Anytime (async backfill) | Only at table creation |
| Capacity | Separate RCU/WCU or on-demand | Shares table throughput |
| Consistency | Eventually consistent reads only | Strongly consistent option |
| Best for | Alternate lookup dimensions | Alternate sort on same entity group |
Index overloading puts multiple entity types on one GSI by varying PK/SK prefixes. A GSI named GSI1 might serve "orders by status," "users by email," and "products by category"—each pattern uses distinct prefix values in gsi1pk and gsi1sk.
GSI design checklist
- Project only attributes the query needs—smaller items mean lower RCU.
- Avoid low-cardinality GSI partition keys (same hot-partition risk as the base table).
- Limit GSI count; each adds write cost and operational surface.
- Test with production-scale cardinality before launch.
For teams building on AWS alongside ECS on Fargate or a data lake on S3, DynamoDB often sits in the hot path while S3 holds analytics archives. Keep OLTP patterns in DynamoDB; export to S3 via DynamoDB Streams and AWS Glue for reporting.
What are common DynamoDB data modeling mistakes?
These show up repeatedly when developers treat DynamoDB like a document store with flexible ad-hoc queries.
Scan-driven features. Admin search across arbitrary fields without a supporting index will not scale. Add OpenSearch or design a dedicated GSI per filter dimension.
Unbounded item growth. Appending to a list attribute on every event creates items that exceed the 400 KB limit. Use a write sharding pattern: SK=EVENT#<shard#>#<timestamp> across fixed shard counts.
Monotonic PKs. Time-based PKs like DATE#2026-09-08 concentrate writes on one partition per day. Add a random suffix: DATE#2026-09-08#<uuid-mod-10>, then query all shards in parallel.
Ignoring TTL. Session data, idempotency keys, and cache rows should carry a ttl attribute. DynamoDB deletes expired items at no direct charge—cheaper than manual cleanup jobs.
MongoDB fits schema-flexible workloads where relational modeling feels wrong—see vector databases for PHP developers for another non-relational angle. DynamoDB wins when you need predictable latency, AWS-native integration, and serverless scaling without cluster ops.
How do you implement DynamoDB data modeling in a real project?
Walk through a concrete workflow. Assume a multi-vendor marketplace similar in shape to directory platforms I have shipped.
Step 1: Capture access patterns
Workshop with product and backend leads. Export the list to your ticket system. Every story that implies "search everything" gets flagged for index design or a search engine.
Step 2: Draft the single-table layout
/*
* MarketplaceTable — access patterns:
* 1. Get vendor profile by ID
* 2. List products by vendor
* 3. List products by category (browse page)
* 4. Get product detail by slug
*/
// Vendor profile
{ PK: "VENDOR#v-12", SK: "PROFILE", name: "...", rating: 4.7 }
// Product under vendor partition
{ PK: "VENDOR#v-12", SK: "PRODUCT#p-88", title: "...", priceNpr: 1200,
gsi1pk: "CATEGORY#electronics", gsi1sk: "PRODUCT#p-88",
gsi2pk: "SLUG#wireless-earbuds", gsi2sk: "PRODUCT#p-88" } Step 3: Define infrastructure as code
Resources:
MarketplaceTable:
Type: AWS::DynamoDB::Table
Properties:
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
- AttributeName: SK
AttributeType: S
- AttributeName: gsi1pk
AttributeType: S
- AttributeName: gsi1sk
AttributeType: S
- AttributeName: gsi2pk
AttributeType: S
- AttributeName: gsi2sk
AttributeType: S
KeySchema:
- AttributeName: PK
KeyType: HASH
- AttributeName: SK
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: GSI1
KeySchema:
- AttributeName: gsi1pk
KeyType: HASH
- AttributeName: gsi1sk
KeyType: RANGE
Projection:
ProjectionType: INCLUDE
NonKeyAttributes: [title, priceNpr, vendorId]
- IndexName: GSI2
KeySchema:
- AttributeName: gsi2pk
KeyType: HASH
- AttributeName: gsi2sk
KeyType: RANGE
Projection:
ProjectionType: KEYS_ONLY Step 4: Load-test before launch
Use AWS SDK retry logic with exponential backoff. Run awslabs/dynamodb-benchmark or Artillery scripts against staging. Watch ThrottledRequests and p99 latency. Fix key design before go-live—not after a Dashain traffic spike.
For enterprise application development, pair DynamoDB with proper testing and optimization. Seed realistic NPR-priced catalog data and validate pagination behavior under 10k-item partitions.
Official references worth bookmarking: the DynamoDB best practices guide and the core components documentation from AWS. For single-table theory, the Amazon builders library articles on adjacency lists remain the clearest explanation of why one table beats many.
Key Takeaways
- List every access pattern before choosing PK, SK, and GSI shapes—scans are a design failure, not a workaround.
- Use high-cardinality partition keys; shard writes when natural keys cluster on one value.
- Denormalize and duplicate items when two query paths both need single-digit millisecond latency.
- Prefer GSIs for alternate lookups; reserve LSIs for rare cases needing strong consistency on an alternate sort.
- Set TTL on ephemeral rows and use
TransactWriteItemswhen duplicate items must stay in sync. - Load-test partition distribution early; hot partitions cause throttling that no amount of on-demand billing fully hides.
People Also Ask
Is single-table design required for DynamoDB?
No. Single-table design reduces table sprawl and fits many microservice boundaries, but multi-table setups are valid when access patterns are isolated. Start single-table when patterns share infrastructure; split only when lifecycle, backup, or IAM boundaries demand it.
How is DynamoDB pricing affected by data modeling?
Every GSI doubles write cost for indexed attributes. Item size drives RCU per read. Efficient keys and sparse projections directly lower monthly bills—often more than switching between on-demand and provisioned capacity.
Can DynamoDB replace PostgreSQL or MySQL?
Not for general-purpose OLTP with ad-hoc reporting. DynamoDB excels at known, high-volume access paths. Keep relational databases for complex transactions, flexible reporting, and workloads that need multi-row ACID across unrelated entities.
What tools help design DynamoDB schemas?
NoSQL Workbench from AWS visualizes single-table layouts and generates sample data. DynamoDB Local runs offline for integration tests. Pair them with IaC templates and contract tests that assert each access pattern hits Query or GetItem.
Build the Right Data Layer for Your Next Project
Amazon DynamoDB data modeling for developers rewards upfront discipline. Map access patterns, design keys that spread load, and treat indexes as first-class cost centers—not afterthoughts. Whether you are adding a high-throughput cache beside Laravel, building a marketplace API, or modernizing webhook processing, the same rules apply: query-driven design beats entity-driven hope.
Need help choosing between DynamoDB, PostgreSQL, and Redis for your stack—or implementing the access patterns correctly? Review our marketplace directory portfolio work and custom software development services, then contact us to talk through your data layer before the first table goes live.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

