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
- Leia o cenário inicial com suas restrições
- Escolha entre as opções disponíveis (ou proponha a sua)
- Leia as consequências da sua decisão
- 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:
- Você tendeu a escolher a opção mais complexa ou a mais simples?
- Você considerou as restrições (tempo, equipe, orçamento) ou apenas os requisitos técnicos?
- Você mudaria alguma decisão sabendo das consequências?
- 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.