Diego Moraes
Enviado em 17 de out. de 2026, 08:51
Avaliado em 17 de out. de 2026, 09:00
A nota é uma recomendação da IA; a decisão é sua.
Avaliação
Nota final = 70% diagrama + 30% defesa das perguntas.
Notas por dimensão
Pontos fortes
- Uso adequado de mensageria (Kafka) desacoplando o processamento assíncrono do sistema de pagamentos externo.
- Separação das regras de campanha em banco relacional com cache no Redis com TTL curto, permitindo alterações dinâmicas pelo marketing sem necessidade de deploy.
- Adoção de abordagem append-only de lançamentos/transações para compor o histórico financeiro do cliente.
Pontos fracos
- Tratamento de concorrência e chave de unicidade incompatível: foi afirmado que a tabela de transações possui `purchase_id` único, mas logo em seguida é dito que reembolsos gravam um registro com o mesmo `purchase_id`. Se a restrição única for estrita no `purchase_id`, o estorno falhará por colisão de chave primária/única.
- Falha em lidar com desordem de eventos (out-of-order delivery): a restrição explicitou que um reembolso pode chegar antes da confirmação da compra, mas o fluxo não descreve como tratar essa inversão cronológica sem gerar inconsistência ou falha de processamento.
- Condição de corrida no resgate: a validação de saldo seguida de inserção direta no checkout sem bloqueio atômico ou mecanismo de reserva (pessimistic lock, compare-and-swap ou chave de idempotência com saldo condicional no banco) permite que requisições concorrentes deixem o saldo negativo, violando o NFR.
Sugestões
- Modelar a chave de idempotência composta (ex: `purchase_id` + `event_type` ou `operation_id`), permitindo registrar tanto a compra quanto eventuais reembolsos parciais ou totais para o mesmo pedido.
- Armazenar um saldo materializado na carteira com verificação atômica (`UPDATE wallet SET balance = balance - amount WHERE balance >= amount`) ou padrão de reserva durante o checkout para garantir atomicidade sob concorrência.
- Inverter o fluxo no diagrama onde o Redis consulta o Postgres: caches não buscam dados sozinhos; deve ser o 'Serviço de carteira' que busca no banco em caso de cache miss e popula o Redis (padrão Cache-Aside).
- Definir uma Dead-Letter Queue (DLQ) para mensagens que falharem repetidamente no cálculo ou persistência de regras de cashback.
Sinais de integridade
Registrados pelo navegador do candidato durante o desenho e a defesa. São sinais para revisar, não prova: um segundo monitor, uma notificação ou colar as próprias anotações também geram registros. E a ausência de registros não garante nada, porque outro dispositivo, como um celular, não aparece. Esses registros não mudam a nota.
- Saiu da aba ou da janela 1× por 12 s no total.
Linha do tempo
Tempo contado desde o início de cada etapa.
- Desenho 19:10Saiu da aba por 12 s
Diagrama e explicação
No celular o diagrama aparece reduzido. Dá para usar dois dedos para zoom, mas a leitura fica melhor no computador.
Explicação do candidato
Os pagamentos publicam os eventos de compra no Kafka e um worker consome, calcula o cashback com as regras e grava na tabela de transações com purchase_id único, então se o evento repetir o insert falha e eu ignoro. As regras ficam no Postgres com cache no Redis de 1 minuto, assim o marketing muda sem deploy. Reembolso gera uma transação negativa com o mesmo purchase_id e tipo estorno. Reembolso parcial calcula proporcional ao valor devolvido. O saldo fica no Redis para responder rápido no app. Quando entra transação nova eu invalido a chave e o próximo acesso recalcula somando as transações do cliente. No checkout o serviço de carteira confere o saldo e grava uma transação de resgate negativa. Para a Black Friday escalo os workers horizontalmente pelo número de partições.
Perguntas de defesa
1. Como sua arquitetura lida com a condição de corrida no checkout para garantir que o saldo nunca fique negativo se o cliente tentar realizar dois resgates simultâneos?
Do jeito que desenhei, o serviço lê o saldo do Redis, então dois resgates ao mesmo tempo podem passar. Eu mudaria para um UPDATE condicional numa tabela de saldo no Postgres: UPDATE saldos SET saldo = saldo - :valor WHERE cliente = :id AND saldo >= :valor. Se não afetar nenhuma linha, o resgate é recusado. É atômico, e o Redis vira só cache de leitura.
Avaliação da resposta9.0
A resposta identifica com precisão a vulnerabilidade da leitura em cache e propõe a solução canônica com UPDATE condicional atômico no banco de dados relacional. Faltou apenas detalhar a reserva temporária versus débito definitivo caso o checkout externo falhe após a dedução.
2. Você mencionou invalidar a chave do Redis e recalcular o saldo somando todas as transações no Postgres na leitura seguinte; como evitar gargalos no banco e picos de latência (cache stampede) na Black Friday com esse modelo?
Somar tudo a cada leitura não aguenta a Black Friday. Eu passaria a manter o saldo materializado no Postgres, atualizado na mesma transação do insert, e o Redis com TTL curto. Para o stampede, um lock com SET NX para que só uma requisição recalcule quando a chave expira; as outras leem o valor antigo por alguns segundos.
Avaliação da resposta8.5
A solução resolve com eficiência o gargalo ao materializar o saldo na escrita e adotar bloqueio com SET NX para mitigar o cache stampede. Poderia ter especificado uma estratégia de atualização direta do cache (write-through) para dispensar completamente recálculos síncronos na rota de leitura.
3. Se um evento de reembolso ou estorno chegar antes da confirmação da compra (fora de ordem), como o worker processará o cálculo proporcional e a persistência sem quebrar a restrição de purchase_id único?
Eu deixaria a unicidade por (purchase_id, tipo). Se o estorno chega antes, o worker não acha a compra e devolve a mensagem para retry com atraso, até a confirmação chegar e o estorno poder ser calculado.
Avaliação da resposta8.0
A resposta corrige a chave de unicidade para uma restrição composta e adota fila de retry com atraso para aguardar a confirmação da compra. Deixou de contemplar o tratamento com Dead Letter Queue caso a compra de origem nunca chegue ou seja cancelada.