Simulador de Decisões

Enfrente cenários reais nos quais você precisa tomar decisões arquiteturais e avaliar suas consequências a curto e longo prazo.

Objetivo

Neste laboratório você vai enfrentar uma série de cenários nos quais precisa tomar decisões arquiteturais sob restrições reais: tempo, orçamento, equipe e requisitos que mudam. Cada decisão tem consequências que se revelam nas rodadas seguintes.

Como funciona

  1. Leia o cenário inicial com suas restrições
  2. Escolha entre as opções disponíveis (ou proponha a sua)
  3. Leia as consequências da sua decisão
  4. Enfrente o próximo cenário que decorre da sua escolha anterior

Não há respostas corretas universais — mas há, sim, decisões mais bem fundamentadas do que outras.


Cenário 1: O monólito que cresce

Contexto

Sua empresa tem um monólito em Node.js que já está há 3 anos em produção. A equipe cresceu de 4 para 12 desenvolvedores. Os deploys demoram 45 minutos e os conflitos de merge são constantes. O CTO quer “migrar para microsserviços”.

Restrições

  • Orçamento: não há verba para contratar mais gente
  • Timeline: 6 meses para mostrar resultados
  • O monólito gera US$ 2M/ano em receita — não pode cair

Opções

Opção A: Big bang — reescrever tudo em microsserviços

  • Risco: alto (reescrita completa)
  • Tempo estimado: 8-12 meses
  • Benefício potencial: arquitetura limpa do zero

Opção B: Strangler fig — extrair serviços gradualmente

  • Risco: médio (mudanças incrementais)
  • Tempo estimado: 3-6 meses para o primeiro serviço
  • Benefício potencial: valor incremental, risco controlado

Opção C: Monólito modular — reorganizar sem distribuir

  • Risco: baixo (refactoring interno)
  • Tempo estimado: 2-3 meses
  • Benefício potencial: melhor organização sem complexidade distribuída

Consequências

Se você escolheu a Opção A

Aos 4 meses, a equipe concluiu 30% do trabalho. O monólito continua recebendo novas features porque o negócio não pode esperar. Agora você tem dois sistemas para manter e a equipe está dividida. O CTO começa a perguntar quando vai ficar pronto.

Lição: Reescritas completas quase nunca terminam no prazo. O negócio não para enquanto você reescreve.

Se você escolheu a Opção B

Aos 3 meses, você extraiu o serviço de notificações. Os deploys do monólito ficaram um pouco mais rápidos. A equipe aprendeu sobre comunicação entre serviços, service discovery e monitoramento distribuído. Agora você pode planejar a próxima extração com mais confiança.

Lição: O padrão Strangler Fig permite migrar gradualmente com risco controlado. Cada extração ensina lições para a próxima.

Se você escolheu a Opção C

Aos 2 meses, o monólito está organizado em módulos com interfaces claras. Os conflitos de merge caíram 60%. Os deploys ainda demoram 45 minutos, mas agora você pode fazer deploys parciais por módulo. A equipe tem uma base sólida para extrair serviços no futuro, se necessário.

Lição: Às vezes a melhor arquitetura distribuída é a que você não precisa. Um monólito bem organizado pode escalar mais do que você imagina.


Cenário 2: Escolher o banco de dados

Contexto

Você está projetando um novo serviço de inventário. Ele precisa lidar com:

  • 500.000 SKUs com atualizações frequentes
  • Consultas complexas com filtros e agregações
  • Histórico de alterações para auditoria
  • Leituras 10x mais frequentes do que escritas

Opções

Opção A: PostgreSQL

  • Modelo relacional, ACID completo
  • Excelente para consultas complexas
  • Escalonamento vertical + read replicas

Opção B: MongoDB

  • Modelo de documentos, schema flexível
  • Escalonamento horizontal nativo (sharding)
  • Consultas de agregação poderosas

Opção C: PostgreSQL + Redis

  • PostgreSQL como fonte da verdade
  • Redis como cache de leitura
  • Maior complexidade operacional

Consequências

Se você escolheu a Opção A

O PostgreSQL lida bem com as 500K linhas e com as consultas complexas. Aos 6 meses, as leituras começam a saturar o banco de dados. Você adiciona uma read replica e o problema é resolvido. O histórico de alterações você implementa com uma tabela de auditoria e triggers.

Lição: PostgreSQL é uma escolha sólida para a maioria dos casos. Não subestime até onde um banco de dados relacional bem configurado pode chegar.

Se você escolheu a Opção B

O MongoDB funciona bem no início. Aos 3 meses, você descobre que as consultas de agregação com múltiplos filtros são mais lentas do que o esperado. O schema flexível causou inconsistências nos dados porque diferentes partes da equipe salvavam os documentos com estruturas ligeiramente diferentes.

Lição: A flexibilidade de schema é uma faca de dois gumes. Sem disciplina, gera dívida técnica. O MongoDB brilha quando o modelo de dados é genuinamente hierárquico.

Se você escolheu a Opção C

A combinação funciona muito bem para as leituras. Mas aos 2 meses, você descobre bugs de invalidação de cache: alguns usuários veem dados stale depois das atualizações. Você implementa um sistema de invalidação baseado em eventos e o problema é resolvido, mas a complexidade operacional aumentou significativamente.

Lição: Adicionar cache resolve problemas de desempenho, mas introduz problemas de consistência. Só adicione cache quando realmente precisar, não “só por precaução”.


Cenário 3: O serviço externo que falha

Contexto

Seu serviço de pagamentos depende de um provedor externo (Stripe). Nas últimas semanas, o Stripe teve 3 incidentes de disponibilidade que afetaram seus usuários. A equipe de produto quer que “os pagamentos nunca falhem”.

Opções

Opção A: Adicionar um segundo provedor (PayPal) como fallback

  • Você duplica a integração de pagamentos
  • Se o Stripe falhar, redirecionar para o PayPal automaticamente
  • Maior complexidade na reconciliação

Opção B: Implementar fila de reenvios

  • Os pagamentos que falham vão para uma fila
  • São reprocessados automaticamente com backoff exponencial
  • O usuário recebe notificação quando o pagamento é processado

Opção C: Circuit breaker + degradação graceful

  • Detectar falhas do Stripe rapidamente
  • Mostrar ao usuário uma mensagem clara e oferecer nova tentativa mais tarde
  • Salvar a tentativa para processá-la quando o Stripe voltar

Consequências

Se você escolheu a Opção A

Funciona, mas a reconciliação entre dois provedores é complexa. Alguns pagamentos são processados no Stripe e outros no PayPal, o que complica os relatórios financeiros. Além disso, o PayPal tem sua própria taxa de falhas — não é imune. Aos 3 meses, você tem um sistema mais resiliente, porém significativamente mais complexo de operar.

Lição: Múltiplos provedores aumentam a disponibilidade, mas multiplicam a complexidade. Avalie se o custo operacional justifica a melhoria na disponibilidade.

Se você escolheu a Opção B

Os reenvios automáticos resolvem 90% das falhas transitórias. Mas você descobre um problema: alguns pagamentos são processados duas vezes porque o reenvio chega quando o Stripe já havia processado o primeiro (mas respondeu com timeout). Você implementa idempotência com chaves únicas e o problema é resolvido.

Lição: Reenvios sem idempotência são perigosos. Sempre implemente idempotência antes de implementar reenvios automáticos.

Se você escolheu a Opção C

O circuit breaker detecta as falhas em segundos e para de enviar requests ao Stripe. Os usuários veem uma mensagem clara: “O sistema de pagamentos está temporariamente indisponível. Seu carrinho está salvo — você pode concluir a compra mais tarde.” A experiência não é perfeita, mas é honesta e não gera frustração.

Lição: Às vezes a melhor resposta a uma falha é ser transparente com o usuário. Nem todos os problemas exigem soluções técnicas complexas.


Reflexão final

Depois de completar os cenários, reflita:

  1. Você tendeu a escolher a opção mais complexa ou a mais simples?
  2. Você considerou as restrições (tempo, equipe, orçamento) ou apenas os requisitos técnicos?
  3. Você mudaria alguma decisão sabendo das consequências?
  4. Que padrão você vê nas suas decisões? Você é conservador ou arrojado?

As melhores decisões arquiteturais não são as mais sofisticadas — são as que melhor se adaptam ao contexto real do projeto.