Single-Table Design dans DynamoDB
Venant du SQL, l'instinct est une table par entité : customers, orders,
order_items. Dans DynamoDB, cet instinct est généralement faux. Une seule table
qui stocke chaque entité, distinguée par des préfixes de clé surchargés, te
laisse récupérer un parent et ses enfants en un seul Query — pas de
jointures, pas de N+1.
Pars des modes d'accès, pas des entités
Le single-table design est orienté modes-d'accès-d'abord. Avant de choisir une
clé unique, note chaque lecture que ton app effectue — « récupérer le profil d'un
client », « lister les commandes d'un client du plus récent au plus ancien »,
« trouver toutes les commandes ouvertes » — car les clés n'existent que pour servir
cette liste. La normalisation relationnelle optimise le stockage ; la modélisation
DynamoDB optimise les requêtes que tu sais déjà devoir exécuter. Énumère-les, puis
conçois les clés pour que chacune soit un seul Query.
L'idée
Choisis des noms de clé génériques (PK, SK) et encode le type d'entité dans la
valeur :
| PK | SK | attributes |
|---|---|---|
| CUSTOMER#42 | PROFILE | name, email, plan |
| CUSTOMER#42 | ORDER#2026-001 | total, status |
| CUSTOMER#42 | ORDER#2026-002 | total, status |
Maintenant un seul Query PK = "CUSTOMER#42" renvoie le profil et chaque
commande en une seule lecture facturée. SK begins_with "ORDER#" le restreint aux
seules commandes.
Visuellement, les items surchargés s'empilent sous une seule clé de partition comme une seule collection d'items :
Une seule lecture de la partition renvoie le client et chaque commande ensemble.
GSI surchargés
La même astuce fonctionne sur les index. Mets un GSI1PK/GSI1SK générique sur les
items, et un seul GSI sert plusieurs modes d'accès selon ce que chaque item écrit
dans ces attributs :
| PK | SK | GSI1PK | GSI1SK |
|---|---|---|---|
| ORDER#001 | METADATA | STATUS#OPEN | 2026-01-04 |
| ORDER#002 | METADATA | STATUS#OPEN | 2026-01-05 |
Maintenant Query GSI1 WHERE GSI1PK = "STATUS#OPEN" liste les commandes ouvertes
par date — un pattern auquel la table de base ne peut pas répondre. Une entité
différente peut réutiliser GSI1 avec sa propre signification (par ex.
CATEGORY#books). Un index, plusieurs requêtes.
Plusieurs-à-plusieurs : la liste d'adjacence
Pour les relations (un utilisateur dans plusieurs équipes, une équipe avec
plusieurs utilisateurs), écris l'arête deux fois avec les ids inversés :
PK=USER#1, SK=TEAM#9 et PK=TEAM#9, SK=USER#1. Requêter un côté liste l'autre —
le substitut DynamoDB d'une table de jointure.
Quand ne pas faire de single-table
Ce n'est pas gratuit. Une table surchargée est plus difficile à raisonner, plus difficile à faire évoluer et hostile à l'analytique. Si tes modes d'accès sont réellement inconnus ou changent constamment, ou si les données sont surtout analytiques, des tables séparées (ou un autre stockage) peuvent être le choix plus sain. Le single-table gagne quand les patterns sont connus et à fort volume.
Coût de la mauvaise forme
Modéliser en tables séparées force un Scan ou une jointure côté client pour
réassembler un client, et c'est le piège du Scan. Modélise
d'abord les modes d'accès, puis conçois les clés pour que chacun soit un Query.
Estime ce que ces items coûtent par lecture avec le calculateur de taille d'item et de capacité, et essaie DynoTable pour parcourir un schema single-table et voir les collections surchargées côte à côte.