WP-002 in Data & Storage

A Single-Table DynamoDB Model

Access patterns first, schema second

Version
0.3
Status
draft
Published
3 min read · 452 words
This document is a draft and may change.

Abstract

This paper works through a single-table DynamoDB design from access patterns to keys, GSIs, and the cost model that makes it attractive for a zero-cost-when-idle stack. It is a draft: the entity set is stable but the overloaded index strategy is still being validated against real query volume.

Why single-table

Relational instincts say “one table per entity.” DynamoDB rewards the opposite: co-locating related entities in a single table lets you satisfy a query with one request and no joins.1 The cost is up-front design effort — you must know your access patterns before you choose keys.

Draft status
The access patterns and entities below are settled. The overloaded GSI strategy in §4 is provisional and may change once query telemetry exists.

Access patterns

Design begins with a list of questions the application asks, not with entities:

#Access patternFrequency
A1Get a paper by idHigh
A2List papers in a collection, newest firstHigh
A3List papers by authorMedium
A4List papers carrying a topicMedium
A5Get an author profileLow
Table 1. Enumerated access patterns the model must serve.

The model

Two entities — Paper and Author — share one table. The primary key is overloaded: PK/SK mean different things per item type.

Table
single, on-demand billing
Partition key
PK (string, overloaded)
Sort key
SK (string, overloaded)
GSI1
GSI1PK / GSI1SK (collection + date, author lookups)
Billing
pay-per-request (no provisioned floor)

The item shapes:

Paper    PK = PAPER#<id>            SK = PAPER#<id>
         GSI1PK = COLLECTION#<name> GSI1SK = DATE#<iso8601>
Author   PK = AUTHOR#<handle>       SK = AUTHOR#<handle>
Membership  PK = PAPER#<id>         SK = AUTHOR#<handle>   (A3)
PK = PAPER#wp-002SK=PAPER#wp-002 · title, abstract, version, status …SK=AUTHOR#jon · role=authorSK=AUTHOR#guest · role=contributor
Figure 1. One partition holds a paper and its author memberships, read in a single query.

Resolving each pattern

  • A1GetItem on PK = PAPER#<id>, SK = PAPER#<id>.
  • A2Query GSI1 on GSI1PK = COLLECTION#<name>, ScanIndexForward=false for newest-first.
  • A3Query GSI1 on GSI1PK = AUTHOR#<handle> (the membership items carry an author-scoped GSI1PK).
  • A4 — topics are low-cardinality and queried rarely; a sparse GSI2 keyed on TOPIC#<name> covers A4 without bloating GSI1.
The provisional bit
Overloading GSI1 for both A2 (collection) and A3 (author) keeps index count low but couples two read paths. If author listings grow hot, A3 should move to its own index. This is the open question blocking promotion past draft.

Cost model

On-demand billing means there is no provisioned-capacity floor: an idle table costs only storage, which for a document catalog is negligible.2 Reads and writes are billed per request, so the bill scales with actual traffic — the same zero-idle property the rest of the stack targets.

Open questions

  1. Does GSI1 overloading survive real author-listing volume? (§4)
  2. Should topic membership be denormalized onto the paper item to drop GSI2?
  3. Is a TTL attribute warranted for superseded drafts?

  1. The canonical treatment of single-table design and the access-pattern-first method. 1 ↩︎

  2. AWS guidance on when single-table design applies and its billing implications. 2 ↩︎

References

  1. A. DeBrie (2022). The DynamoDB Book. self-published.
  2. Amazon Web Services (2024). Single-table vs. multi-table design in DynamoDB. Amazon DynamoDB Developer Guide.
Cite this paper
Jon (2026). A Single-Table DynamoDB Model (v0.3). Prosyon Research. https://research.prosyon.ca/papers/single-table-dynamodb/