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

  1. Zero downtime durante a migração
  2. Capacidade de rollback a qualquer momento
  3. Os dados históricos de pedidos devem estar disponíveis no novo serviço
  4. 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:

  1. Crie o novo serviço de pedidos
  2. Redirecione as leituras primeiro (menor risco)
  3. Em seguida, redirecione as escritas
  4. Por fim, remova o código de pedidos do monolito
Dica 2: Migração de dados

Use Change Data Capture (CDC):

  1. Faça um dump inicial dos pedidos históricos para o novo serviço
  2. Configure o CDC para replicar as alterações em tempo real do monolito para o novo serviço
  3. Quando ambos estiverem sincronizados, redirecione o tráfego
Dica 3: Verificação

Use o padrão Shadow Traffic:

  1. Envie uma cópia de cada request ao novo serviço (sem afetar o usuário)
  2. Compare as respostas do monolito e do novo serviço
  3. 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:

  1. Idempotência: um pagamento não pode ser processado duas vezes
  2. Resiliência: se um provedor falhar, tentar outro automaticamente
  3. Auditoria: cada tentativa de pagamento deve ficar registrada
  4. Conciliação: poder verificar que as cobranças coincidem com os pedidos
  5. PCI compliance: não armazenar dados de cartão no seu sistema

Sua tarefa

Projete:

  1. A API do serviço de pagamentos
  2. O fluxo interno de processamento
  3. O modelo de dados
  4. As estratégias de resiliência
  5. 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:

  1. Cartão de crédito: Stripe → PayPal
  2. Cartão de débito: Stripe → PayPal
  3. 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

  1. Defina a API para enviar notificações
  2. Projete o fluxo de processamento interno
  3. Defina como lidar com as preferências do usuário
  4. Projete a estratégia de rate limiting
  5. 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

  1. Quais métricas você coletaria de cada serviço?
  2. Como você implementaria distributed tracing?
  3. Quais alertas você configuraria?
  4. Como você correlacionaria logs entre serviços?
  5. 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 traceId no API Gateway
  • Propagar via header X-Trace-Id para 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:

  1. Qual foi o desafio mais difícil? Por quê?
  2. Em quais áreas você sente que precisa de mais prática?
  3. Você conseguiu reutilizar padrões de seções anteriores?
  4. 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.