Teve um momento em que ficou claro, para mim, que eu estava usando o Amazon DynamoDB do jeito errado. Não por desconhecimento da API, mas por carregar um modelo mental que não se aplicava ali. Eu tentava listar dados com filtros livres, resolvia quase tudo criando índices e, quando surgia throttling, minha reação era olhar para throughput e capacidade provisionada, não para o desenho dos dados.
O ajuste veio quando eu mudei a pergunta inicial. Em vez de “qual é a tabela?”, passei a perguntar “quais são os acessos?”. No DynamoDB, essa pergunta não é conceitual nem abstrata. Ela define diretamente custo, latência e estabilidade do sistema. Modelagem aqui não é uma etapa formal do projeto; ela é o próprio funcionamento do banco.
A proposta deste capítulo é exatamente essa: caminhar pela modelagem passo a passo, explicando os conceitos que sustentam decisões como uso de GSIs, índices esparsos, projections e sharding, sempre com o objetivo de evitar os dois problemas mais comuns em produção: throttling inesperado e crescimento descontrolado de custo.
Antes de existir tabela, já existe acesso
Em bancos relacionais, o fluxo clássico começa pelo domínio. Desenhamos entidades, relacionamentos, normalizamos dados e, só depois, quando as consultas aparecem, ajustamos índices. Esse modelo funciona porque o otimizador do banco tenta encontrar o melhor caminho possível para cada query.
No DynamoDB, essa lógica se inverte. Não existe otimizador decidindo caminhos. Existe um contrato muito claro: cada operação precisa saber exatamente onde buscar ou escrever dados. Por isso, o ponto de partida não é a estrutura, mas as perguntas que a aplicação precisa responder.
Essas perguntas não são SQL. Elas são intenções reais do sistema. O que precisa ser lido? O que precisa ser escrito? Isso acontece com que frequência? Em picos ou de forma distribuída? Existe ordenação envolvida? Essas respostas moldam o desenho.
Quando você olha por esse ângulo, percebe que quase todos os acessos no DynamoDB se encaixam em três padrões. Ou você acessa um item específico, ou acessa um conjunto de itens relacionados, ou varre grandes volumes de dados. Esse último existe, mas deve ser exceção, não regra.
Essa separação é importante porque o DynamoDB foi projetado para oferecer um acesso previsível por requisição, mesmo em volumes extremamente altos. Ele não oferece liberdade irrestrita de consulta, mas entrega algo muito mais valioso em sistemas críticos: previsibilidade de comportamento.
Um sistema de pedidos como exemplo conceitual
Para tornar os conceitos concretos, vale usar um domínio simples e recorrente: pedidos. Pode ser e-commerce, logística, marketplace ou seguros. A estrutura muda pouco.
A aplicação precisa criar pedidos, atualizar status, registrar informações adicionais e guardar identificadores externos, como invoice ou pagamento. Do lado das leituras, ela precisa buscar um pedido específico, listar pedidos de um cliente, alimentar uma fila operacional para processamento e reagir a eventos externos que chegam com identificadores que não são internos.
Esse exercício já deixa claro um ponto fundamental: não existe um único caminho de leitura. E tentar resolver todos esses acessos com uma única chave costuma ser o primeiro erro em DynamoDB.
A chave primária define o caminho mais barato do sistema
No DynamoDB, a chave primária da tabela base define o acesso mais direto, mais barato e mais confiável. É o caminho que você quer que seja trivial.
Em sistemas de pedidos, esse papel quase sempre é do orderId. Quando você escolhe esse identificador como partition key da tabela principal, qualquer leitura ou atualização de um pedido específico vira uma operação direta. Não existe plano de execução, não existe dúvida sobre onde o dado está. A latência é previsível e o custo é conhecido.
Essa decisão não resolve todos os acessos, e isso é esperado. Ela resolve o acesso canônico. Todos os outros passam a ser tratados como rotas alternativas, e é exatamente aí que entram os índices secundários globais.
Global Secondary Indexes como rotas de leitura
Um erro comum é enxergar GSIs como “índices SQL”. Eles não são. Um GSI é uma rota de leitura explícita que você cria conscientemente, sabendo que cada escrita na tabela pode gerar escrita adicional no índice.
Por isso, a pergunta correta nunca é “precisamos de um GSI?”. A pergunta é “esse acesso acontece com frequência suficiente para justificar pagar por ele em cada escrita?”.
Listar pedidos de um cliente é um bom exemplo de acesso que justifica um índice. Quando você usa o identificador do cliente como partition key do GSI e combina o timestamp de criação com o orderId na sort key, você cria um agrupamento natural. Os pedidos daquele cliente ficam fisicamente próximos, ordenados no tempo, e a paginação acontece de forma estável. Não há busca genérica seguida de filtro. Há um caminho direto até o subconjunto correto dos dados.
Filas operacionais e o problema da concentração
O ponto onde a maioria dos incidentes reais começa é a fila operacional. Sistemas crescem e passam a ter estados como “pendente”, “em processamento”, “em retry”. A solução inicial parece óbvia: criar um índice por status e ordenar por prioridade ou data.
Funciona enquanto o volume é baixo. O problema surge quando valores como PENDING ou IN_PROGRESS se tornam muito frequentes. Mesmo com toda a escala interna do DynamoDB, você acabou de criar um ponto lógico de concentração. Muitas leituras e muitas escritas disputando o mesmo valor de partition key.
É nesse cenário que aparece o conceito de hot partition. E esse problema não se resolve apenas aumentando capacidade, mas redistribuindo carga.
A forma correta de fazer isso é introduzir sharding no partition key do índice. O status continua existindo como conceito lógico, mas ele passa a ser combinado com um sufixo que distribui os itens em múltiplos grupos. O custo cognitivo dessa decisão é pequeno, mas o impacto em estabilidade é enorme.
O ponto importante é entender que isso não é uma gambiarra. É o uso direto do mecanismo de distribuição por chave que o DynamoDB oferece.
Índices esparsos: nem todo item precisa estar em toda rota
Uma das otimizações mais importantes em DynamoDB é perceber que um índice não precisa conter todos os itens da tabela. Ele só precisa conter os itens relevantes para aquele acesso.
Em uma fila operacional, pedidos finalizados não precisam existir. Quando apenas itens em estados ativos possuem os atributos que compõem a chave do índice, você cria um índice esparso. Ao mudar o status e remover esses atributos, o item simplesmente deixa de fazer parte daquela rota de leitura.
Isso reduz escrita, reduz armazenamento e reduz leitura desnecessária. E tudo isso acontece sem lógica adicional de “limpeza” no código. É o próprio modelo que governa o comportamento.
Projection: trazendo apenas o que realmente é usado
Outro ponto que separa modelos saudáveis de modelos caros é a projeção dos índices. Projetar o item inteiro “por conveniência” costuma ser um desperdício.
Listas e filas raramente precisam de todos os atributos. Elas precisam de identificadores, estado, ordenação e alguns metadados. Payloads grandes, históricos detalhados e estruturas complexas não agregam valor nesse contexto.
Quando você projeta apenas os atributos necessários, reduz o tamanho dos itens no índice. Isso impacta diretamente custo de escrita, custo de leitura e até o número de páginas retornadas por consulta. Para quem vem de DBA, essa ideia é quase instintiva: traga apenas o que é usado. No DynamoDB, isso se traduz em economia concreta.
Quando o modelo não está bom, os sintomas aparecem rápido
Com o sistema em produção, os sinais aparecem cedo. A latência piora justamente nos picos. O custo cresce mais rápido que o volume de dados. Surgem throttles difíceis de explicar mesmo com capacidade aparentemente suficiente.
Na maioria dos casos, a causa raiz está no modelo. Partition keys com baixa cardinalidade, filtros aplicados depois da leitura, índices replicando dados que quase nunca são usados ou itens grandes sendo atualizados com muita frequência.
A boa notícia é que, quando os conceitos estão claros, esses problemas deixam de ser misteriosos. Eles passam a ser decisões técnicas que podem ser revistas e corrigidas.
Um desenho previsível é, quase sempre, simples
Quando todos esses conceitos se juntam, o resultado costuma ser mais simples do que parece à primeira vista. Uma tabela base com um identificador canônico bem definido. Um índice para histórico do cliente. Um índice operacional com sharding e sparse index para suportar picos. Um índice específico para integrações externas, evitando scans.
Cada rota existe porque atende a um acesso real. Cada índice tem um custo conhecido e justificado. Não há nada “por via das dúvidas”.
É nesse ponto que o DynamoDB deixa de parecer difícil e passa a ser previsível.
A diferença entre times que sofrem com DynamoDB e times que usam o serviço com tranquilidade raramente está no domínio da API. Ela está na forma de pensar.
Quando você modela para acesso, entende GSIs como rotas explícitas, usa sparse index e projection como ferramentas estratégicas e aceita sharding como parte natural de sistemas que crescem, o banco começa a trabalhar a seu favor.
Checklist de review de modelagem DynamoDB
1. Qual é o acesso canônico da tabela base?
Antes de olhar qualquer GSI, é fundamental entender qual acesso a tabela resolve de forma direta. A chave primária da tabela deve atender o acesso mais frequente, mais crítico e mais simples do sistema. Esse é o caminho que precisa ser barato, rápido e previsível. Se a tabela base não resolve nenhum acesso real com eficiência, o modelo já começa errado.
2. Cada GSI existente corresponde a um acesso real da aplicação?
Todo GSI precisa ter um “dono”: uma tela, uma API, um fluxo de integração ou uma fila operacional. Índices criados “para o futuro” ou “por segurança” tendem a gerar custo constante sem retorno. Se não existe código que use esse índice hoje, ele precisa ser questionado.
3. Com que frequência esse acesso acontece em produção?
GSIs são pagos na escrita. Mesmo acessos raros geram custo contínuo se o índice replica todos os itens. É importante entender se o acesso ocorre milhares de vezes por minuto ou apenas em operações administrativas. A frequência do acesso precisa justificar o custo de manter o índice sempre atualizado.
4. A partition key do GSI tem cardinalidade suficiente?
Esse é um dos pontos mais críticos. Se muitos itens compartilham o mesmo valor de partition key, você cria um ponto lógico de concentração. Valores como status, tipo ou categoria costumam ser perigosos quando usados sozinhos. Cardinalidade baixa é um forte indicativo de hot partition futura.
5. Esse GSI precisa de sharding?
Se o acesso agrupa muitos itens sob um mesmo valor lógico, é necessário avaliar sharding. O sharding permite distribuir carga mantendo o significado do acesso. Não é um anti-pattern; é uma técnica normal em modelos maduros e quase sempre necessária em filas operacionais ou estados muito frequentes.
6. Todos os itens da tabela realmente precisam existir nesse GSI?
Um índice não precisa conter toda a tabela. Se apenas um subconjunto dos itens faz sentido para aquele acesso, o modelo deve refletir isso. Índices esparsos reduzem escrita, armazenamento e leitura. Se um item nunca será consultado por aquele acesso, ele não deveria estar no índice.
7. Os atributos do GSI são imutáveis ou mudam com frequência?
Quando atributos que fazem parte da chave do índice mudam com frequência, cada mudança gera escrita adicional no GSI. Isso aumenta custo e risco de throttling. É importante avaliar se a chave representa algo estável ou algo que sofre muitas transições ao longo do ciclo de vida do item.
8. A projeção do GSI inclui apenas os atributos necessários?
Projetar o item inteiro quase nunca é necessário. Listas, filas e visões operacionais costumam precisar de poucos campos. Projetar somente o que será lido reduz tamanho dos itens, custo de escrita e custo de leitura. Se um atributo não é usado no acesso, ele não deveria estar no índice.
9. Esse acesso poderia ser resolvido sem GSI?
Nem todo acesso justifica um índice. Se o conjunto de dados é pequeno, previsível ou cabe em uma única página de leitura, pode ser mais barato ler e filtrar na aplicação. Criar um GSI para acessos raros ou de baixo volume costuma ser desperdício.
10. Esse GSI replica dados grandes desnecessariamente?
Itens grandes amplificam custo. Se o índice replica payloads extensos, históricos ou blobs que não são usados no acesso, o impacto financeiro cresce rapidamente. O tamanho dos itens no índice deve ser revisado com a mesma atenção que a cardinalidade da chave.
11. Quantos GSIs estão sendo atualizados a cada escrita?
Cada escrita na tabela pode gerar múltiplas escritas em índices. É importante olhar o efeito acumulado. Três ou quatro GSIs ativos significam três ou quatro escritas extras por operação. Se a taxa de escrita é alta, isso precisa estar explícito no desenho e no custo esperado.
12. Existe algum GSI com comportamento parecido com outro?
Às vezes dois índices atendem acessos muito semelhantes. Nesses casos, pode ser possível reutilizar um único GSI com pequenas adaptações no código. Reduzir redundância de índices diminui custo e complexidade operacional.
13. O modelo evita filtros pós-leitura?
Filtros aplicados depois da leitura não reduzem custo nem latência. Se um acesso depende fortemente de filtros, provavelmente ele não está bem modelado. O ideal é que a chave do índice já direcione para o subconjunto correto dos dados
14. Esse acesso precisa de ordenação previsível?
Se a ordem dos resultados importa, ela precisa estar refletida na sort key. Ordenação não deve ser feita na aplicação após leitura. A sort key define como os dados são fisicamente organizados e paginados, e isso é parte central do modelo.
15. O time sabe explicar por que esse GSI existe?
Essa é uma pergunta simples, mas poderosa. Se ninguém consegue explicar claramente qual problema o índice resolve, ele provavelmente não deveria existir. Um bom modelo é aquele que qualquer engenheiro do time consegue justificar.
16. Esse modelo foi pensado para o pico, não para a média?
Muitos problemas surgem quando o sistema cresce ou entra em momentos de pico. O modelo precisa funcionar bem quando o volume é alto, não apenas em cenários ideais. Se o acesso já parece frágil em escala pequena, ele será instável em escala real.
17. Existe um plano claro para evolução desse modelo?
Mudanças acontecem. O modelo deve permitir evolução sem backfills desnecessários ou migrações complexas. GSIs devem ser adicionados com intenção clara e removidos quando deixam de fazer sentido. Modelos rígidos demais tendem a virar dívida técnica.
18. O custo desse modelo é compreendido pelo time?
O time precisa entender que GSIs custam escrita, leitura e armazenamento. Se o custo esperado não está claro, o modelo não está completo. Um bom desenho no DynamoDB sempre vem acompanhado de consciência de custo.
Conclusão
Modelar no Amazon DynamoDB não é um exercício de estrutura, é um exercício de intenção. Diferente do mundo relacional, onde o banco tenta “dar um jeito” nas consultas que surgem, no DynamoDB cada caminho de acesso precisa ser pensado, assumido e pago conscientemente.
Quando problemas aparecem em produção (throttling inesperado, latência instável ou custo crescendo mais rápido que o negócio) a causa raramente está na capacidade configurada. Na maioria das vezes, ela está no desenho: chaves com baixa cardinalidade, índices que existem sem um acesso claro, dados grandes sendo replicados sem necessidade ou filtros sendo aplicados tarde demais.
GSIs não são atalhos nem otimizações automáticas. Eles são rotas explícitas de leitura, que precisam existir porque resolvem um problema real da aplicação. Sparse indexes, projections e sharding não são técnicas avançadas opcionais; são ferramentas fundamentais para manter previsibilidade em escala.
O checklist apresentado não existe para burocratizar decisões, mas para tornar o raciocínio explícito. Ele ajuda times a discutirem modelo com base em comportamento do sistema, não em suposições. Quando cada pergunta é respondida com clareza, o DynamoDB deixa de ser uma caixa-preta e passa a ser um componente confiável da arquitetura.
No fim, o DynamoDB não exige que você acerte tudo de primeira. Ele exige que você saiba por que cada coisa existe. Quando o modelo reflete os acessos reais, o banco faz exatamente o que promete: escalar sem drama, manter latência previsível e permitir que o time foque no que realmente importa .







Deixe um comentário