Desafios de Arquitetura
Desafios avançados que combinam múltiplos conceitos — projete soluções completas para cenários complexos do mundo real.
Objetivo
Os desafios desta seção combinam múltiplos conceitos de arquitetura em cenários complexos. Diferentemente dos laboratórios anteriores (que focam em um único tema), aqui você precisará integrar padrões, resiliência, segurança e operações em soluções completas.
Cada desafio tem um nível de dificuldade e um tempo estimado. Tente resolvê-los antes de ver as dicas.
Desafio 1: Migração sem downtime
Nível: Intermediário | Tempo estimado: 30-45 minutos
Cenário
Sua empresa tem um monolito que cuida de autenticação, catálogo e pedidos. Você precisa extrair o serviço de pedidos como microsserviço sem downtime. O monolito processa 500 pedidos por hora e não pode parar.
Requisitos
- Zero downtime durante a migração
- Capacidade de rollback a qualquer momento
- Os dados históricos de pedidos devem estar disponíveis no novo serviço
- Os relatórios existentes devem continuar funcionando
Sua tarefa
Projete um plano de migração passo a passo. Considere:
- Como migrar os dados sem parar o monolito?
- Como redirecionar o tráfego gradualmente?
- Como verificar que o novo serviço funciona corretamente?
- Qual é o seu plano de rollback?
Dicas
Dica 1: Padrão de migração
Use o padrão Strangler Fig:
- Crie o novo serviço de pedidos
- Redirecione as leituras primeiro (menor risco)
- Em seguida, redirecione as escritas
- Por fim, remova o código de pedidos do monolito
Dica 2: Migração de dados
Use Change Data Capture (CDC):
- Faça um dump inicial dos pedidos históricos para o novo serviço
- Configure o CDC para replicar as alterações em tempo real do monolito para o novo serviço
- Quando ambos estiverem sincronizados, redirecione o tráfego
Dica 3: Verificação
Use o padrão Shadow Traffic:
- Envie uma cópia de cada request ao novo serviço (sem afetar o usuário)
- Compare as respostas do monolito e do novo serviço
- Quando as respostas coincidirem em 99,9%, redirecione o tráfego real
Solução de referência
Ver solução completa
Fase 1 — Preparação (semana 1-2):
- Criar o novo serviço de pedidos com a mesma API
- Configurar o CDC a partir da tabela de pedidos do monolito
- Migrar dados históricos com um script batch
Fase 2 — Verificação (semana 3):
- Ativar o shadow traffic: cada request ao monolito é duplicado para o novo serviço
- Comparar respostas automaticamente e registrar discrepâncias
- Corrigir bugs até que as respostas coincidam
Fase 3 — Migração gradual (semana 4):
- Redirecionar 10% do tráfego de leitura para o novo serviço
- Monitorar latência, erros e consistência dos dados
- Aumentar gradualmente: 25% → 50% → 75% → 100%
Fase 4 — Escritas (semana 5):
- Redirecionar as escritas para o novo serviço
- O novo serviço publica eventos que o monolito consome (para relatórios legados)
- Monitorar intensivamente durante 48 horas
Fase 5 — Limpeza (semana 6):
- Remover o código de pedidos do monolito
- Desativar o CDC
- Atualizar os relatórios para usar a nova fonte de dados
Rollback em qualquer fase: Reverter a configuração do proxy/load balancer para enviar o tráfego ao monolito.
Desafio 2: Sistema de pagamentos multiprovedor
Nível: Avançado | Tempo estimado: 45-60 minutos
Cenário
Projete um serviço de pagamentos que suporte múltiplos provedores (Stripe, PayPal, transferência bancária) com os seguintes requisitos:
- Idempotência: um pagamento não pode ser processado duas vezes
- Resiliência: se um provedor falhar, tentar outro automaticamente
- Auditoria: cada tentativa de pagamento deve ficar registrada
- Conciliação: poder verificar que as cobranças coincidem com os pedidos
- PCI compliance: não armazenar dados de cartão no seu sistema
Sua tarefa
Projete:
- A API do serviço de pagamentos
- O fluxo interno de processamento
- O modelo de dados
- As estratégias de resiliência
- O fluxo de conciliação
Dicas
Dica 1: Idempotência
Use uma idempotency key gerada pelo cliente:
POST /payments
Headers: Idempotency-Key: uuid-unique-per-payment
Body: { orderId, amount, currency, provider }
Antes de processar, verifique se já existe um pagamento com essa key. Se existir, retorne o resultado anterior sem processar novamente.
Dica 2: Modelo de dados
Payment {
id: UUID
idempotencyKey: string (unique)
orderId: string
amount: decimal
currency: string
status: PENDING | PROCESSING | COMPLETED | FAILED | REFUNDED
provider: STRIPE | PAYPAL | BANK_TRANSFER
providerTransactionId: string?
attempts: PaymentAttempt[]
createdAt: timestamp
updatedAt: timestamp
}
PaymentAttempt {
id: UUID
paymentId: UUID
provider: string
status: SUCCESS | FAILED | TIMEOUT
errorCode: string?
errorMessage: string?
responseTime: number
attemptedAt: timestamp
}
Dica 3: Estratégia de fallback
Defina uma ordem de prioridade de provedores por tipo de pagamento:
- Cartão de crédito: Stripe → PayPal
- Cartão de débito: Stripe → PayPal
- Transferência: apenas banco
Se o provedor primário falhar com erro recuperável (timeout, 503), tentar o próximo. Se falhar com erro não recuperável (cartão recusado, saldo insuficiente), não repetir a tentativa.
Desafio 3: Projete um sistema de notificações
Nível: Intermediário | Tempo estimado: 30-45 minutos
Cenário
Projete um serviço de notificações que suporte:
- Canais: email, push, SMS, in-app
- Prioridades: crítica (imediata), alta (< 1 min), normal (< 5 min), baixa (batch diário)
- Preferências: os usuários podem escolher quais canais receber por tipo de notificação
- Rate limiting: não enviar mais de 10 notificações por hora por usuário
- Templates: as notificações usam templates com variáveis dinâmicas
Sua tarefa
- Defina a API para enviar notificações
- Projete o fluxo de processamento interno
- Defina como lidar com as preferências do usuário
- Projete a estratégia de rate limiting
- Defina o que acontece quando um canal falha
Considerações
- Você usa uma fila por prioridade ou uma fila com prioridades?
- Como lidar com o batch diário de notificações de baixa prioridade?
- O que acontece se o serviço de email estiver fora do ar, mas o de push funcionar?
- Como evitar enviar a mesma notificação duas vezes?
Ver arquitetura sugerida
[API de Notificações]
│
▼
[Fila de prioridade]
├── Fila CRITICAL (processamento imediato, workers dedicados)
├── Fila HIGH (processamento < 1 min)
├── Fila NORMAL (processamento < 5 min)
└── Fila LOW (acumulação para batch)
│
▼
[Notification Processor]
│
├── Verificar preferências do usuário
├── Aplicar rate limiting
├── Resolver template com variáveis
│
▼
[Channel Router]
├──► [Email Provider] (SendGrid/SES)
├──► [Push Provider] (Firebase/APNs)
├──► [SMS Provider] (Twilio)
└──► [In-App Store] (WebSocket/DB)
Rate limiting: Usar Redis com um contador por usuário com TTL de 1 hora. Antes de enviar, verificar o contador. Se exceder o limite, enfileirar para o próximo período.
Deduplicação: Usar uma chave composta (userId + notificationType + entityId) com TTL para evitar duplicatas.
Desafio 4: Observabilidade end-to-end
Nível: Avançado | Tempo estimado: 45-60 minutos
Cenário
Você tem 8 microsserviços em produção. Um usuário relata que “às vezes o checkout demora muito”. Você não consegue reproduzir o problema de forma consistente. Projete uma estratégia de observabilidade que permita diagnosticar esse tipo de problema.
Sua tarefa
- Quais métricas você coletaria de cada serviço?
- Como você implementaria distributed tracing?
- Quais alertas você configuraria?
- Como você correlacionaria logs entre serviços?
- Qual dashboard você criaria para o time de plantão?
Critérios de sucesso
Seu design deve permitir:
- Identificar qual serviço causa a latência em menos de 5 minutos
- Ver o trace completo de um request do usuário
- Detectar degradações antes que os usuários as relatem
- Entender o impacto de um deploy na latência do sistema
Ver estratégia sugerida
Métricas (por serviço):
- Latência: p50, p90, p95, p99 por endpoint
- Taxa de erros: 4xx e 5xx por endpoint
- Throughput: requests por segundo
- Saturação: CPU, memória, conexões de DB, pool de threads
Distributed tracing:
- Gerar um
traceIdno API Gateway - Propagar via header
X-Trace-Idpara todos os serviços - Cada serviço registra spans com início, fim e metadata
- Usar OpenTelemetry como padrão
Alertas:
- Latência p99 > 2x do baseline por mais de 5 minutos
- Taxa de erros 5xx > 1% por mais de 2 minutos
- Fila de mensagens crescendo por mais de 10 minutos
- CPU > 80% por mais de 5 minutos
Dashboard de plantão:
- Visão geral: health de todos os serviços (verde/amarelo/vermelho)
- Visão de fluxo: latência por etapa do checkout
- Visão de traces: os 10 traces mais lentos da última hora
- Visão de erros: erros agrupados por tipo e serviço
Reflexão
Depois de completar os desafios:
- Qual foi o desafio mais difícil? Por quê?
- Em quais áreas você sente que precisa de mais prática?
- Você conseguiu reutilizar padrões de seções anteriores?
- Qual desafio se parece mais com um problema real que você enfrenta no seu trabalho?
Os desafios foram projetados para simular a complexidade real da arquitetura de software. Não existem soluções perfeitas — existem soluções bem fundamentadas.