AWS na Prática · 02 — DynamoDB
Modelagem NoSQL para os microserviços do nosso sistema de pedidos.
Modelagem NoSQL para os microserviços do nosso sistema de pedidos.
Sobre este módulo
Aqui você vai entender como o DynamoDB pensa diferente de bancos relacionais e como modelar dados a partir dos access patterns. Vamos criar as três tabelas do projeto e validá-las com operações reais via console e CLI.
Nível: Intermediário · Duração estimada: 3-4 horas
Sumário
- Por que NoSQL para microserviços?
- Modelo mental do DynamoDB
- Partition key e sort key
- Capacity modes
- Global Secondary Indexes (GSIs)
- Modelagem por access patterns
- Modelando as tabelas do projeto
- Criando as tabelas no console
- Operações via CLI
- Exercícios de fixação
- Próximos passos
01. Por que NoSQL para microserviços?
Antes de aprender DynamoDB especificamente, vale entender por que bancos NoSQL fazem tanto sentido em arquiteturas de microserviços — e por que essa não é uma escolha automática.
O encaixe natural com microserviços
Microserviços e NoSQL não são exigência um do outro, mas se complementam por razões estruturais. Bancos NoSQL como o DynamoDB foram desenhados para escalar horizontalmente, oferecer latência previsível e schema flexível — exatamente o que microserviços precisam.
Em um banco relacional, normalizar e fazer JOINs é o caminho natural. Em um mundo de microserviços, JOINs entre serviços não existem — cada serviço tem seu banco. NoSQL te empurra a desnormalizar dados intencionalmente, que é exatamente o que precisamos.
Quando NoSQL não é a melhor escolha
Há cenários em que SQL ainda vence: relatórios analíticos com queries ad-hoc, dados altamente relacionais com integridade referencial complexa, ou quando o time tem muito mais experiência com SQL e o ganho de escala não justifica a curva de aprendizado.
02. Modelo mental do DynamoDB
Esquece tudo que você sabe sobre bancos relacionais por dez minutos. DynamoDB tem um modelo diferente — não pior nem melhor, apenas diferente. Forçar padrões SQL aqui leva a designs ruins.
Tabelas, itens e atributos
- Tabela — uma coleção de itens. Não tem schema rígido — itens diferentes podem ter atributos diferentes.
- Item — uma “linha” da tabela. É um objeto com qualquer número de atributos. Cada item tem no máximo 400 KB.
- Atributo — um par chave-valor dentro de um item. Pode ser string, number, boolean, lista, mapa, set, binary.
- Primary key — identificador único de cada item. É o único atributo obrigatório. Pode ser simples (só partition key) ou composta (partition + sort).
O que NÃO existe no DynamoDB
- JOINs — você precisa modelar para evitá-los, ou fazer múltiplas queries.
- Transações multi-tabela complexas — há TransactWrite, mas com limites estritos.
- SQL — a linguagem é via SDK, com operações como PutItem, GetItem, Query, Scan.
- Schema rígido — você define apenas as chaves; o resto é livre por item.
- Foreign keys — não existe integridade referencial automática.
- Auto-increment — IDs precisam ser gerados pela aplicação (geralmente UUIDs).
A inversão de pensamento
Em SQL, você modela primeiro (schema normalizado) e depois pensa nas queries. Em DynamoDB, é o contrário: você lista todas as queries que vai fazer (access patterns), e a partir disso desenha as tabelas. É uma inversão completa de fluxo.
03. Partition key e sort key
A escolha das chaves é a decisão mais importante na modelagem DynamoDB. Errar aqui significa hot partitions, queries impossíveis ou custos altíssimos. Acertar aqui é metade do trabalho.
Partition key (hash key)
É o atributo que determina em qual partição física o item será armazenado. O DynamoDB aplica um hash sobre esse valor para distribuir os itens entre partições internas — o que permite a escala horizontal.
Para queries eficientes, você precisa da partition key. Sem ela, só resta fazer Scan — uma operação cara e lenta que lê a tabela inteira.
Sort key (range key)
Opcional. Quando presente, junto com a partition key forma a chave primária composta. Permite que múltiplos itens compartilhem a mesma partition key, ordenados pela sort key.
É aqui que mora boa parte do poder do DynamoDB: usando sort key inteligente, você consegue queries como “todos os pedidos do cliente X entre data Y e Z, ordenados por data” com performance constante.
Hot partitions: o vilão
Se sua partition key tem pouca cardinalidade (poucos valores diferentes) ou está mal distribuída, alguns itens recebem muito mais tráfego que outros. Isso é uma hot partition — uma partição sobrecarregada enquanto as outras ficam ociosas.
Exemplo ruim: usar status (PENDING, PAID, CANCELLED) como partition key. Como há só 3 valores possíveis, milhões de pedidos vão se concentrar em 3 partições. Resultado: throttling.
Exemplo bom: usar orderId (UUID) como partition key. Cada pedido vai para uma partição diferente — distribuição uniforme.
Operações: Get, Query, Scan
| Operação | O que faz | Custo |
|---|---|---|
| GetItem | Pega um item exato pela chave primária completa | Mínimo (1 RCU) |
| Query | Busca itens dentro de uma partition key, com filtro opcional na sort key | Baixo |
| Scan | Lê a tabela inteira aplicando filtros depois | Alto — evite! |
Scan é o último recurso. Se você precisa fazer scan na sua aplicação regularmente, sua modelagem está errada. Use GSIs (próxima seção) para criar “views” adicionais sobre os dados.
04. Capacity modes
Como você paga pelo DynamoDB? Há dois modos, com filosofias bem diferentes. Entender quando usar cada um pode reduzir muito sua fatura.
Provisioned capacity
Você reserva uma quantidade fixa de capacidade: RCUs (Read Capacity Units) e WCUs (Write Capacity Units). Paga por hora, independente de uso.
1 RCU = 1 leitura forte de item até 4 KB por segundo, ou 2 leituras eventualmente consistentes. 1 WCU = 1 escrita de item até 1 KB por segundo.
É o modo barato quando o tráfego é previsível. Se você sabe que tem 100 leituras/seg constantes, provisiona 100 RCUs e paga pouco.
On-demand capacity
Você não reserva nada. Paga por requisição — cerca de 7x mais caro que provisioned com o mesmo tráfego, mas sem provisionar errado.
É o modo certo quando o tráfego é imprevisível ou esporádico — exatamente o nosso caso em ambiente de estudo.
Qual usar no projeto?
| Cenário | Modo recomendado |
|---|---|
| Aprendizado / dev pessoal | On-demand |
| Produção com tráfego previsível | Provisioned com auto-scaling |
| Produção com picos imprevisíveis | On-demand |
| Cargas batch periódicas | Provisioned |
| Free Tier | Provisioned (25 RCU/WCU grátis) |
Throttling
Se você ultrapassar a capacidade provisionada, o DynamoDB começa a rejeitar requisições com erro ProvisionedThroughputExceededException. O SDK faz retry automático com backoff, mas se for muito tráfego, suas operações vão começar a falhar.
05. Global Secondary Indexes
GSIs são o mecanismo que torna DynamoDB realmente flexível. Sem eles, você ficaria preso a queries pela chave primária. Com eles, pode criar “views” alternativas sobre os mesmos dados.
O que é um GSI?
Um Global Secondary Index é uma cópia automática da sua tabela com uma chave primária diferente. Você define quais atributos da tabela “projetam” no índice, e o DynamoDB mantém o índice sincronizado automaticamente.
Quando usar GSI
Sempre que você precisar de um access pattern que não usa a chave primária principal. Exemplo concreto:
- Tabela Orders com partition key orderId.
- Access pattern necessário: “listar todos os pedidos do cliente X”.
- Sem GSI, você teria que fazer Scan filtrando por customerId — caro e lento.
- Com GSI customerId-index (PK=customerId), uma Query simples resolve.
Custos de um GSI
GSIs não são grátis:
- Cada GSI tem suas próprias RCUs e WCUs (provisioned mode).
- Cada escrita na tabela principal gera escritas no GSI — você paga 2x as WCUs (ou mais, se houver vários GSIs).
- Armazenamento adicional, proporcional aos atributos projetados.
Eventual consistency em GSIs
GSIs são sempre eventualmente consistentes. Há um pequeno atraso entre a escrita na tabela principal e a propagação para o índice — geralmente milissegundos, mas pode ser mais em casos de carga.
Se você precisa de consistência forte, leia da tabela principal. GSIs servem para queries onde um pequeno atraso é aceitável (lista de pedidos, filtros, relatórios em tempo quase-real).
06. Modelagem por access patterns
Esta é a metodologia. É como um arquiteto AWS pensa quando vai criar uma tabela DynamoDB do zero.
Os 5 passos
- Liste todas as entidades do domínio (Order, Customer, Product, etc).
- Liste todos os access patterns que sua aplicação vai precisar (ex: “buscar pedido por ID”, “listar pedidos do cliente”, “listar pedidos pendentes”).
- Identifique a partition key principal que serve à maioria dos patterns.
- Use sort key para resolver patterns secundários dentro da mesma partição.
- Crie GSIs para os patterns restantes que não cabem na chave principal.
Exemplo: tabela Orders
Vamos aplicar o método. Quais são os access patterns para Orders?
- AP1: buscar pedido por orderId.
- AP2: listar pedidos de um cliente, ordenados por data.
- AP3: listar pedidos por status (ex: todos os pedidos PENDING).
- AP4: buscar pedidos por intervalo de data.
Agora pensamos: orderId resolve AP1. Para AP2, podemos usar GSI com PK customerId e SK createdAt. AP3 sugere outro GSI com PK status e SK createdAt — mas atenção: status tem baixa cardinalidade, então pode gerar hot partition. Decisão: aceitar para o projeto, ou usar uma técnica chamada write sharding para distribuir.
Single-table design (vs multi-table)
Há duas escolas de modelagem em DynamoDB. Multi-table: uma tabela por entidade, modelo familiar para quem vem de SQL. Single-table design: todas as entidades de um domínio em uma única tabela, com chaves polimórficas.
Single-table é mais avançado, mais performático e mais barato — mas exige experiência. No nosso projeto vamos usar multi-table por ser mais didático: uma tabela por microserviço, alinhado ao princípio de “database per service”.
07. Modelando as tabelas do projeto
Vamos formalizar a modelagem das três tabelas. Estes são os schemas que vão ser implementados nos próximos módulos.
Tabela: Orders
Access patterns:
- AP1: buscar pedido por orderId
- AP2: listar pedidos de um cliente por data
- AP3: listar pedidos por status (uso administrativo) Schema:
| Atributo | Tipo | Papel |
|---|---|---|
| orderId | String (UUID) | Partition key (principal) |
| customerId | String (UUID) | PK do GSI 'by-customer' |
| createdAt | String (ISO 8601) | SK do GSI 'by-customer' |
| status | String | PK do GSI 'by-status' |
| items | List<Map> | Itens do pedido |
| totalAmount | Number | Valor total |
| paymentId | String | Referência ao pagamento |
GSIs:
- by-customer — PK: customerId, SK: createdAt
- by-status — PK: status, SK: createdAt
Tabela: Inventory
Access patterns:
- AP1: buscar produto por productId
- AP2: listar produtos por categoria
- AP3: listar produtos com estoque baixo Schema:
| Atributo | Tipo | Papel |
|---|---|---|
| productId | String (UUID) | Partition key (principal) |
| category | String | PK do GSI 'by-category' |
| name | String | Nome do produto |
| price | Number | Preço atual |
| stockQuantity | Number | Quantidade em estoque |
| lowStockFlag | String (Y/N) | Atributo esparso para GSI |
GSIs:
- by-category — PK: category, SK: name
- low-stock — PK: lowStockFlag (sparse index)
Tabela: Payments
Access patterns:
- AP1: buscar pagamento por paymentId
- AP2: listar pagamentos de um pedido Schema:
| Atributo | Tipo | Papel |
|---|---|---|
| paymentId | String (UUID) | Partition key (principal) |
| orderId | String (UUID) | PK do GSI 'by-order' |
| amount | Number | Valor cobrado |
| status | String | PENDING, COMPLETED, FAILED |
| method | String | credit_card, pix, etc. |
| processedAt | String (ISO 8601) | Timestamp |
GSI:
- by-order — PK: orderId, SK: processedAt
08. Criando as tabelas no console
Hora de criar as tabelas. Vamos pelo console primeiro para você ver visualmente como tudo funciona. Em módulos futuros, vamos refazer isso via CloudFormation.
Passo a passo: tabela Orders
- Acesse o console da AWS, faça login com seu usuário otavio-dev, e vá ao serviço DynamoDB.
- Confirme que está na região us-east-1 (canto superior direito).
- Clique em Create table.
- Table name: Orders
- Partition key: orderId (String)
- Sort key: deixe em branco (a tabela principal não precisa).
- Em Settings, escolha Customize settings.
- Table class: Standard.
- Capacity mode: Provisioned.
- Read capacity: 5 RCU. Write capacity: 5 WCU.
- Auto scaling: desabilite (para o estudo).
- Adicione tags: Project=order-system, Environment=dev.
- Clique em Create table. Aguarde o status virar Active.
Adicionando os GSIs
Após a tabela ficar ativa, vá em Indexes > Create index:
GSI 1: by-customer
- Partition key: customerId (String)
- Sort key: createdAt (String)
- Index name: by-customer-index
- Projected attributes: All
- Read/Write capacity: 5 RCU / 5 WCU
GSI 2: by-status
- Partition key: status (String)
- Sort key: createdAt (String)
- Index name: by-status-index
- Projected attributes: All
- Read/Write capacity: 5 RCU / 5 WCU
Repita para Inventory e Payments
Use os schemas da seção 07. As três tabelas (com seus GSIs) caberão confortavelmente dentro do Free Tier. Não esqueça as tags — vamos usá-las depois para encontrar e deletar tudo de uma vez.
09. Operações via CLI
Antes de escrever código, vamos validar as tabelas usando a CLI. Isso te dá familiaridade com as operações fundamentais.
Listar suas tabelas
aws dynamodb list-tables --profile aws-curso
# Saída esperada:
{
"TableNames": ["Orders", "Inventory", "Payments"]
}Inserir um item: PutItem
DynamoDB tem uma sintaxe peculiar para os tipos. Cada valor vem com seu “type descriptor”:
- S — String
- N — Number (sempre como string)
- BOOL — Boolean
- M — Map (objeto)
- L — List (array) Exemplo de inserção em Orders:
aws dynamodb put-item \
--profile aws-curso \
--table-name Orders \
--item '{
"orderId": {"S": "ord-001"},
"customerId": {"S": "cust-123"},
"createdAt": {"S": "2026-01-15T10:30:00Z"},
"status": {"S": "PENDING"},
"totalAmount": {"N": "299.90"}
}'Buscar um item: GetItem
aws dynamodb get-item \
--profile aws-curso \
--table-name Orders \
--key '{"orderId": {"S": "ord-001"}}'Query no GSI: pedidos de um cliente
aws dynamodb query \
--profile aws-curso \
--table-name Orders \
--index-name by-customer-index \
--key-condition-expression "customerId = :cid" \
--expression-attribute-values \
'{":cid": {"S": "cust-123"}}'Note como precisamos especificar --index-name para usar o GSI, e como os valores são passados via expression-attribute-values (placeholders evitam injeção e melhoram cache de query plan).
Deletar um item
aws dynamodb delete-item \
--profile aws-curso \
--table-name Orders \
--key '{"orderId": {"S": "ord-001"}}'10. Exercícios de fixação
Os exercícios deste módulo são obrigatórios antes do próximo. Sem tabelas funcionando, o módulo 03 (Beanstalk) não terá onde guardar dados.
Crie as três tabelas
Siga a seção 08 e crie Orders, Inventory e Payments com todos os GSIs descritos. Confira que todas estão com status Active antes de prosseguir.
Insira dados de teste
Via CLI, insira 5 produtos em Inventory (categorias variadas, alguns com estoque baixo), 3 clientes fictícios fazendo 2 pedidos cada em Orders, e um pagamento por pedido em Payments. Use UUIDs reais (pode usar uuidgen no terminal).
Valide os access patterns
Usando query, valide cada um dos access patterns que modelamos: (a) buscar pedido por orderId; (b) listar pedidos do cliente X; (c) listar pedidos PENDING; (d) produtos da categoria eletronicos; (e) produtos com lowStockFlag=Y. Anote: alguma query falhou? Por quê?
Provoque um throttling
Em uma das tabelas, reduza temporariamente o WCU para 1. Em um shell script, faça 30 PutItem em sequência rápida. Você deve ver ProvisionedThroughputExceededException. Volte o WCU para 5 ao terminar. Isso te ensina o que esperar quando a capacidade é insuficiente.
Reflexão de modelagem
Imagine que o cliente pediu uma nova feature: “ranking dos 10 produtos mais vendidos da semana”. Como você modelaria isso? Adicionaria um GSI? Outra tabela? Faria pré-cálculo? Justifique sua resposta em texto livre.
11. Próximos passos
Banco modelado e funcionando. Hora de subir o primeiro microserviço — Orders — em um ambiente real.
O que você aprendeu
- Modelo mental do DynamoDB e como ele difere de SQL.
- Partition key, sort key, e como evitar hot partitions.
- Capacity modes — provisioned vs on-demand.
- GSIs e quando usá-los.
- Metodologia de modelagem por access patterns.
- Modelagem completa das três tabelas do projeto.
- Operações fundamentais via CLI: PutItem, GetItem, Query.
O que vem no Módulo 03
Elastic Beanstalk a fundo. Vamos subir o primeiro microserviço — Orders — usando Beanstalk. Ciclo de deploy, health checks, environments, logs, troubleshooting. Será o primeiro deploy real do projeto.
Modelagem é arquitetura. Boa jornada — Módulo 03 a seguir.