Design: fases do acesso ao DynamoDB¶
Status: vigente Data: 2026-10-13 ADR: Acesso ao DynamoDB em fases Desenho da lista: Acesso ao DynamoDB
Objetivo¶
Cada fatia abaixo vira uma issue. A issue implementa uma fase, com teste, e pode ir a produção sozinha. A fase 0 não muda chamada que já está no ar.
Fora deste desenho¶
Não há PRD: a tela não muda. page e total continuam na resposta HTTP. GET /orders/summary e GET /orders/aggregate continuam no Athena. Property não ganha GSI de tenant. Lista de produto por status fica de fora até existir índice dessa pergunta. Projeção curta de SalesOrder e FinancialEntry espera ConsumedCapacity. A carga de índice novo não entra no mesmo pull request do caixa. Não nasce classe de CRUD.
Fluxo¶
- Entra o kit em
shared. Nenhum repositório de produção passa a usá-lo nessa fase. - Um repositório que já faz
Querypassa a chamar o kit. O teste confere a mesma chave e o mesmo item. - As listas seguintes trocam
ScanporQueryna chave que já existe, ou esperam índice novo. - Índice novo segue quatro pull requests: criar, gravar a chave nos itens novos, carregar os antigos, apontar a leitura. A leitura antiga permanece até o último passo.
Contrato¶
O kit mora em packages/shared do monorepo. Um DynamoDBDocumentClient com removeUndefinedValues. As operações são get, queryPage, put, update, delete e batch. batch retenta Unprocessed*. O cursor é opaco, em cima de LastEvaluatedKey. ReturnValues é NONE, salvo quando o chamador usa o item devolvido, como o contador de pedidos.
O repositório do domínio monta a chave e chama o kit. RepositoryBase deixa de ser caminho de lista.
Dados¶
A PK das tabelas atuais permanece. Product grava e lê tenant + id. Estoque usa tenant + productId. Preço, variante, BOM e movimento por produto usam o GSI tenant-productId-index. Sales operacional usa a PK tenant, ou o GSI cuja sort key já é status, orderStatus ou createdAt. Delivery, payment e shipment usam a PK tenant e o GSI tenant-orderId. Financial, budget e flipping mantêm unitLookupKey, propertyLookupKey e tenantProjectKey.
Índice novo, um por issue: customerId na raiz do pedido, tenant em person, entity em contract, organizationId e provider em bank accounts. O item carregado ganha schemaVersion.
Riscos¶
Apontar a leitura de pedido por cliente para o índice novo antes da carga some pedido antigo da lista. A fase 3 consulta a partição do tenant e deixa essa troca para a fase 6.
Product que grava PK/SK e a tabela espera tenant + id precisa do teste do mesmo id na fase 2. Sem esse teste, a escrita nova não encontra o item antigo.
Cursor na resposta HTTP quebraria a tela. Nenhuma fatia desta página troca page por cursor.
Fatias¶
Cada linha é uma issue. O aceite é o que o teste prova. A coluna da produção é o que não pode mudar nesse pull request.
| Issue | Fase | Pacote | Aceite | Produção continua |
|---|---|---|---|---|
| Kit de acesso ao DynamoDB | 0 | packages/shared |
Teste de página, cursor, update e batch com retry. Nenhum repositório de produção importa o kit ainda | Toda leitura e escrita atual |
| Cart usa o kit | 1 | packages/sales CartRepository.findByCustomerId |
O teste confere tenant-customerId-index, a mesma chave e o mesmo item |
A lista do carrinho igual |
| Product na chave da tabela | 2 | packages/product |
Escrita e leitura em tenant + id (estoque: productId). Preço, variante, BOM e movimento por produto usam tenant-productId-index. O código não cita productId-index nem ProductStatusIndex. O mesmo id é encontrado |
Lista por status fora. Resposta HTTP igual |
| Pedidos na partição do tenant | 3 | packages/sales leitura de SalesOrder |
Query na PK tenant, ou no GSI cuja sort key já é status, orderStatus ou createdAt. O handler devolve page e total como hoje |
Pedido por cliente no caminho antigo. Summary e aggregate no Athena |
| Delivery, payment e shipment por chave | 4 | packages/delivery e packages/sales |
list por tenant na PK. Busca por orderId no GSI tenant-orderId |
A forma da lista igual |
| Financial, budget e flipping no kit | 5 | packages/financial, packages/budget, packages/flipping |
Usam o kit. Mantêm unitLookupKey, propertyLookupKey e tenantProjectKey. Lista que parava em 1 MB segue o cursor por dentro |
A função devolve a mesma coleção |
Índice customerId em pedido |
6 | packages/sales |
Índice tenant + customerId. Itens novos gravam customerId. Carga dos antigos. A leitura por cliente só muda depois da carga |
Até a carga, a fase 3 permanece |
Índice tenant em person |
6 | packages/person |
Índice novo, carga com schemaVersion, leitura por tenant só depois |
Scan com filtro até o corte |
Índice entity em contract |
6 | packages/contract |
Índice em entity. tenant só se a lista da tela for por tenant. Leitura só depois da carga |
Scan com filtro de entity até o corte |
| Índices de bank accounts | 6 | packages/bank |
Índice em organizationId e provider. Leitura só depois da carga |
Scan com begins_with até o corte |