Como modelar dados no DynamoDB
No SQL você modela entidades e relacionamentos primeiro, depois confia que o planejador de queries montará depois qualquer coisa que você pedir. O DynamoDB inverte isso. Você modela as leituras que já sabe que fará, e as chaves existem para servi-las.
Não há mecanismo de join nem planejador escolhendo uma estratégia em tempo de execução. Uma Query lê uma partição ao longo de uma chave, e esse é todo o contrato de desempenho. Então você projeta chaves para padrões de acesso conhecidos, não para um esquema arrumadinho.
A AWS diz claramente em seu guia de boas práticas: "você não deveria começar a projetar seu schema até saber quais perguntas ele precisará responder".
Este guia percorre todo o processo em um único domínio: um placar de jogo multiplayer acompanhando jogadores, as partidas que eles jogam e seu ranking por temporada. Vamos de uma lista de perguntas a um esquema de chaves funcional.
Como você modela dados no DynamoDB?
Modele as leituras primeiro, não as tabelas. Liste toda query que o app faz, depois projete uma e uma para que cada pergunta se resolva em um único Query ou GetItem. Coloque juntos os itens lidos juntos, percorra faixas de valores na chave de ordenação, e adicione uma GSI para qualquer padrão de acesso que a tabela base não consiga servir.
- Liste as leituras primeiro, não as tabelas. As perguntas são a especificação; os substantivos são uma distração.
- Cada pergunta precisa ser um único
QueryouGetItem. Se uma pergunta precisa de umScan, o modelo está errado. - Itens co-localizados compartilham uma ; qualquer coisa sobre a qual você percorre faixas vai na .
- Uma pergunta que a tabela base não consegue responder ganha uma — nunca um
Scancom filtro.
Passo 1 — Formule o problema como perguntas, não tabelas
Resista à vontade de desenhar tabelas players, matches e scores. Esse instinto é o hábito do SQL, e aqui ele está errado. Em vez disso, anote toda leitura que o app de fato realiza. Para nosso placar:
- Buscar o perfil de um jogador por id.
- Listar as partidas recentes de um jogador, mais novas primeiro.
- Mostrar os N melhores jogadores de uma dada temporada, ranqueados por rating.
- Encontrar um jogador pelo seu handle público (ex.: para uma URL de perfil).
Essas quatro perguntas — não os substantivos — são a especificação. Cada uma precisa se resolver em um único Query (ou GetItem), porque essa é a única forma de acesso que o DynamoDB serve de forma barata em escala.
Se uma pergunta só pode ser respondida escaneando a tabela, o modelo está errado, e você sentirá isso em latência e custo — veja Query vs Scan para entender por que um Scan é a armadilha a evitar.
O método inteiro é um pipeline curto e ordenado que você roda uma vez por domínio:
Cada passo abaixo mapeia para uma caixa: listar, enumerar, projetar chaves, adicionar índices para o resto, depois validar.
Passo 2 — Entenda as primitivas com as quais você está modelando
Uma tabela tem uma chave de partição (PK) que escolhe em qual partição física um item vive, e uma chave de ordenação (SK) opcional que ordena os itens dentro dessa partição.
Os docs de componentes centrais da AWS chamam o par de chave primária do item. Uma Query sempre mira exatamente um valor de PK e pode fazer range-scan ou filtrar a SK — esse é o kit de ferramentas inteiro.
Este design de partição única é o que permite ao DynamoDB entregar as leituras previsíveis, de baixa latência e particionadas horizontalmente descritas pela primeira vez no paper Dynamo da Amazon de 2007.
Duas consequências guiam cada decisão abaixo:
- Itens que são lidos juntos deveriam compartilhar uma chave de partição para que um
Queryos retorne em uma única requisição tarifada. - Qualquer coisa sobre a qual você quer percorrer faixas (partidas recentes, top ratings) precisa viver na chave de ordenação, porque esse é o único atributo que
Querypode ordenar e delimitar.
Quando uma pergunta precisa de uma forma de acesso diferente da que a tabela base fornece, você adiciona uma Global Secondary Index — uma reprojeção da tabela sob uma PK/SK diferente.
(Para GSI versus Local Secondary Index, veja GSI vs LSI.)
Passo 3 — Projete as chaves, uma pergunta de cada vez
Usamos uma única tabela com atributos de chave genéricos e sobrecarregados — a abordagem de tabela única — porque um jogador e suas partidas são lidos juntos.
Invente seus próprios prefixos; aqui PLAYER#, MATCH# e SEASON# etiquetam o tipo de entidade dentro de chaves de resto genéricas.
As perguntas 1 e 2 (perfil + partidas recentes) compartilham uma partição, então ambas se apoiam na mesma PK:
| partitionId | rangeId | attributes |
|---|---|---|
| PLAYER#u8231 | PROFILE | handle, region, createdAt |
| PLAYER#u8231 | MATCH#2026-06-23T14 | result=win, ratingDelta=+18, mapId |
| PLAYER#u8231 | MATCH#2026-06-23T11 | result=loss, ratingDelta=-15, mapId |
Query partitionId = "PLAYER#u8231" retorna o perfil e cada partida em uma única leitura. Para o perfil sozinho, GetItem.
Para partidas recentes, rangeId begins_with "MATCH#" com ScanIndexForward = false as percorre da mais nova primeiro — o timestamp na chave de ordenação faz a ordenação de graça.
As perguntas 3 e 4 não podem ser respondidas a partir dessa partição — elas pivotam em ranking de temporada e em handle, nenhum dos quais é a PK base. Cada uma ganha uma GSI.
Adicionamos dois pares de atributos de índice genéricos — seasonPartition / seasonSort para o índice de ranking e handlePartition / handleSort para o índice de handle — populados no mesmo item de perfil (aquele escrito no Passo 3, agora mostrado com seus atributos de índice preenchidos):
| partitionId | rangeId | seasonPartition | seasonSort | handlePartition | handleSort |
|---|---|---|---|---|---|
| PLAYER#u8231 | PROFILE | SEASON#2026-Q2 | RATING#1842 | HANDLE#nighthawk | PLAYER#u8231 |
Agora Query no índice de temporada WHERE seasonPartition = "SEASON#2026-Q2" com ScanIndexForward = false retorna os jogadores ranqueados por rating — esse é o placar.
Um segundo índice com chave em handlePartition = "HANDLE#…" resolve um handle público para um id de jogador em uma única leitura. Uma tabela física, quatro padrões de acesso de Query único.
Uma nota de sobre
RATING#1842: o DynamoDB ordena as chaves de ordenação lexicograficamente, não numericamente, então um rating precisa ser preenchido com zeros até uma largura fixa (RATING#01842) ou9ordenaria depois de1000. Esta é uma pegadinha de modelagem clássica que vale acertar logo no início.
Passo 4 — Valide o modelo no DynoTable
Um esquema de chaves só ganha confiança quando você vê um Query real retornar exatamente os itens que você esperava e nada mais.
Abra a tabela no DynoTable, rode a query do placar contra o índice de temporada, e confirme que a partição volta ranqueada e delimitada — sem Scan, sem ordenação no lado do cliente.

Quando você construir as expressões de condição para essas queries — o begins_with, o seasonPartition = :p, o binding do placeholder :p — deixe o construtor de expressões do DynamoDB fazer isso.
Ele gera a KeyConditionExpression, as ExpressionAttributeNames e os ExpressionAttributeValues, para que uma palavra reservada como result ou um placeholder com erro de digitação nunca quebre silenciosamente uma leitura.
Passo 5 — Armadilhas e próximos passos
Algumas armadilhas para checar antes de colocar o modelo em produção:
- Não modele relacionamentos que você nunca lê juntos. Uma GSI por pergunta é barata; uma GSI desperdiçada é custo recorrente. Adicione índices a partir da lista de perguntas, não especulativamente.
- Fique de olho no calor da partição. Se uma PK (um jogador celebridade, uma única temporada quente) absorve a maior parte do tráfego, essa partição pode sofrer throttle. Espalhe as escritas com um sufixo de fragmento quando uma chave é comprovadamente quente — a AWS cobre isso em design de chave de partição.
- Preencha com zeros e use ISO-8601 em tudo que é numérico ou temporal em uma chave de ordenação, para que a ordenação lexicográfica corresponda à ordem que você pretende.
- Uma nova pergunta = uma nova chave ou índice, nunca um
Scan. Quando um padrão de acesso genuinamente novo aparece depois, estenda as chaves; não disfarce com um filtro.
Modele as perguntas primeiro, projete chaves para que cada uma seja um único Query, depois prove.
Para adiantar o passo do meio, a gratuita ferramenta de Single-Table Design transforma uma lista de padrões de acesso como esta em um plano PK/SK/GSI, com itens de exemplo e dicas de custo.
Experimente o DynoTable para navegar pela sua tabela, rodar essas queries contra a tabela base e as GSIs lado a lado, e ver os padrões de acesso que você projetou retornarem exatamente o que você planejou. E para a pergunta que você não modelou, seu SQL Workbench roda JOINs, GROUP BY e agregações de verdade no lado do cliente.


