Why this matters
- VaultCommerce's order microservice collapsed five tables into one
VaultCommercetable — fewer cross-table transactions, one on-call surface, lower latency for "load my account page." - The trade-off is cognitive: every engineer must read the access-pattern matrix before adding an entity.
- GSIs on the same table provide inverted lookups (email → user) while keeping writes atomic within a partition where possible.
Key overloading pattern
{ "PK": "USER#42", "SK": "PROFILE", "email": "alice@vault.test", "tier": "GOLD" }
{ "PK": "USER#42", "SK": "ORDER#2026-0042", "total": 129.99, "status": "PAID" }
{ "PK": "USER#42", "SK": "WISHLIST#SKU-8842", "addedAt": "2026-04-01" }
{ "PK": "PRODUCT#SKU-8842", "SK": "METADATA", "title": "Trail Pack", "price": 89 }
Single-table conventions
PK— partition anchor (USER#id,PRODUCT#sku,ORDER#id).SK— entity discriminator and sort order (PROFILE,ORDER#date,METADATA).- GSI1 — inverted lookups (
GSI1PK=EMAIL#alice@vault.test→ find user). - Type attribute — optional
entityType: "Order"for client-side filtering.
One Query PK=USER#42 with SK begins_with prefixes assembles the account dashboard.
Access pattern matrix
| Pattern | Operation | Key condition |
|---------|-----------|---------------|
| User profile | GetItem | PK=USER#42, SK=PROFILE |
| User orders | Query | PK=USER#42, SK begins_with ORDER# |
| Product detail | GetItem | PK=PRODUCT#SKU, SK=METADATA |
| Login by email | Query GSI1 | GSI1PK=EMAIL#... |
Design the matrix before writing code — adding a pattern late often means new GSIs and backfill jobs.
Write pattern with denormalization
VaultCommerce duplicates customerName on order items to avoid a second lookup at read time. Accept duplication; update with TransactWrite when profile renames must propagate (or emit async reconciliation via Streams).
table.put_item(Item={
"PK": f"USER#{user_id}",
"SK": f"ORDER#{order_id}",
"GSI1PK": f"STATUS#{status}",
"GSI1SK": created_at,
"customerName": name,
"total": total,
})
Quick recall
Everything you need if you only revisit this box.
- Single-table design overloads PK/SK prefixes to colocate related entities.
- One Query per partition replaces multiple SQL joins for dashboard reads.
- Maintain an access-pattern matrix before schema changes.
- Denormalize attributes to avoid extra reads; reconcile async when needed.
- GSIs on the same table power inverted lookups (email → user).
- VaultCommerce stores users, orders, and wishlists under
USER#partitions.
Test yourself
Answer these before moving on — recall is what makes it stick.