Microservices vs monolitos: quando usar cada um
1. Introdução ao Debate: Monolitos e Microservices
A arquitetura monolítica é a forma mais tradicional de construir software: toda a aplicação é um único processo, com um único deploy e um único banco de dados compartilhado. Tudo está acoplado no mesmo artefato — um JAR, um executável ou uma imagem Docker monolítica.
Já a arquitetura de microservices divide o sistema em serviços menores, independentes, cada um com seu próprio processo, banco de dados e ciclo de deploy. A comunicação entre eles ocorre via rede (HTTP, mensageria, gRPC).
O falso dilema é pensar que você precisa escolher um dos extremos. Na prática, existe um espectro. Muitas organizações começam com um monolito bem estruturado e extraem microservices conforme a necessidade real surge. A decisão não é técnica pura — envolve domínio de negócio, tamanho da equipe, maturidade operacional e estágio do produto.
2. Quando o Monolito Ainda é a Melhor Escolha
Projetos em estágio inicial (startup / MVP)
Em um MVP, a prioridade é validar hipóteses de negócio rapidamente. Um monolito permite que você entregue funcionalidades completas com muito menos overhead. Compare:
Monolito (MVP):
- 1 repositório
- 1 pipeline CI/CD
- 1 deploy por feature
- Tempo de deploy: ~2 minutos
Microservices (MVP):
- 5 repositórios
- 5 pipelines CI/CD
- Deploy coordenado entre serviços
- Tempo de deploy: ~15 minutos + coordenação
Equipes pequenas (até 8 pessoas)
Comunicação é direta. Não há necessidade de times autônomos. Um monolito reduz a complexidade de versionamento de contratos de API, testes de integração distribuídos e debugging em múltiplos serviços.
Domínios de negócio fortemente acoplados
Sistemas que exigem consistência transacional forte (como um sistema financeiro de contas a pagar) se beneficiam do monolito. Transações ACID em um único banco são muito mais simples que sagas e consistência eventual.
Monolito: transação bancária
BEGIN TRANSACTION;
debitar_conta_origem(conta_A, 100);
creditar_conta_destino(conta_B, 100);
COMMIT;
Microservices: mesma operação exige saga
1. Serviço A debita (preparação)
2. Serviço B credita (compensação se falhar)
3. Confirmação ou rollback em duas fases
3. Quando os Microservices São a Abordagem Ideal
Escalabilidade independente
Serviços com perfis de carga diferentes exigem recursos distintos. Exemplo clássico:
Serviço de processamento de imagens:
- Consumo intensivo de CPU
- Escala horizontal com 10 instâncias
Serviço de autenticação:
- Baixo consumo de CPU
- Escala com 2 instâncias
Monolito: você escala tudo junto → desperdício de recursos
Microservices: escala cada serviço conforme sua demanda real
Múltiplas equipes autônomas
Cada squad possui ownership claro sobre seu serviço. Deploys independentes permitem que o time A lance uma nova feature sem esperar o time B. Isso reduz drasticamente o tempo de entrega.
Resiliência e isolamento de falhas
Em um monolito, um vazamento de memória em uma funcionalidade derruba o sistema inteiro. Em microservices, a falha fica isolada no serviço afetado. Com circuit breakers e fallbacks, o resto do sistema continua operando.
4. Os Custos Ocultos de Cada Arquitetura
Monolito: dívida técnica crescente
Conforme o código cresce, o acoplamento implícito aumenta. Uma mudança em uma parte pode quebrar outra distante. O deploy único se torna arriscado: um bug em uma funcionalidade pode impedir o lançamento de outras 10 funcionalidades prontas.
Métrica típica de monolito maduro:
- Tempo médio de deploy: 4 horas
- Taxa de rollback por deploy: 15%
- Número de desenvolvedores tocando no mesmo arquivo: 12
Microservices: complexidade de rede e observabilidade
Cada chamada entre serviços adiciona latência de rede, possibilidade de timeout e necessidade de retry. Debugging distribuído exige ferramentas como Jaeger, Zipkin ou OpenTelemetry. A consistência eventual introduz bugs sutis que não aparecem em testes locais.
Custos operacionais típicos:
- Infraestrutura: Kubernetes, service mesh (Istio/Linkerd)
- Observabilidade: Prometheus, Grafana, ELK Stack
- CI/CD: pipelines por serviço
- Equipe de plataforma: 2-3 pessoas dedicadas
5. Estratégias de Migração e Coexistência
Padrão Strangler Fig
A abordagem mais segura: não reescreva o monolito inteiro. Identifique funcionalidades com fronteiras de domínio claras e extraia uma por vez.
Fase 1: Módulo de notificações no monolito
- Código misturado no core
Fase 2: Extrair para serviço separado
- Novo serviço: notificacoes-api
- Monolito chama via HTTP (roteamento controlado)
Fase 3: Remover código do monolito
- Toda funcionalidade de notificações removida
- Monolito mais enxuto
Módulos bem definidos dentro do monolito
Mesmo que você nunca migre para microservices, organizar o monolito com fronteiras claras (DDD — Domain-Driven Design) prepara o terreno. Use namespaces/pacotes separados, banco de dados com schemas distintos e interfaces bem definidas.
Híbrido pragmático
Mantenha o core transacional (pedidos, pagamentos, estoque) no monolito. Extraia serviços periféricos (notificações, relatórios, processamento de arquivos) para microservices. Isso reduz o risco enquanto traz benefícios de escalabilidade onde realmente importa.
6. Critérios Objetivos para a Decisão
Fatores de domínio
- O domínio possui fronteiras claras? (DDD — bounded contexts)
- Partes do sistema mudam em frequências diferentes?
- Você precisa escalar partes isoladas do sistema?
Fatores de equipe
- Quantas pessoas? (até 8 → monolito; acima de 15 → microservices)
- A equipe tem maturidade em DevOps e observabilidade?
- Existe capacidade de manter múltiplos pipelines e infraestrutura distribuída?
Fatores técnicos
- Latência máxima aceitável para operações críticas?
- Requisitos de consistência (ACID vs eventual)?
- Orçamento operacional para infraestrutura adicional?
Matriz de decisão simplificada:
| Critério | Monolito | Microservices |
|------------------------------|----------|---------------|
| Equipe < 8 pessoas | Sim | Não |
| Consistência forte | Sim | Não |
| Escalabilidade independente | Não | Sim |
| Deploy independente | Não | Sim |
| Orçamento operacional baixo | Sim | Não |
7. Conclusão e Boas Práticas
A recomendação prática da indústria é clara: comece monolítico, mas bem estruturado. Invista em boas fronteiras de domínio (DDD) desde o início — isso é barato e paga dividendos independentemente da arquitetura final.
Extraia para microservices apenas quando houver dor real: uma equipe que não consegue deployar sem quebrar a outra, um serviço que precisa escalar de forma diferente, ou um domínio que muda em ritmo distinto do resto.
Monitore métricas objetivas:
- Tempo médio de deploy (quanto maior, mais motivação para microservices)
- Frequência de deploys por semana
- Taxa de rollback
- Número de desenvolvedores tocando no mesmo arquivo
Lembre-se: microservices resolvem problemas organizacionais e operacionais, não problemas técnicos. Se sua equipe é pequena e seu domínio coeso, um monolito bem projetado entrega mais valor com menos complexidade.
Referências
- Martin Fowler — Microservices — Artigo seminal que define o conceito de microservices com exemplos e trade-offs claros.
- Martin Fowler — Strangler Fig Application — Padrão de migração incremental de monolitos para microservices, com exemplos práticos.
- Microsoft Docs — Microservices architecture — Guia oficial da Microsoft sobre quando usar microservices vs monolitos, com diagramas de decisão.
- AWS — Microservices vs Monolith — Comparação prática da AWS com casos de uso reais e recomendações de infraestrutura.
- Sam Newman — Building Microservices (O'Reilly) — Livro referência sobre design, operação e migração de microservices, com capítulos dedicados à decisão monolito vs microservices.
- Uber Engineering — Microservice Architecture at Uber — Estudo de caso real de migração de monolito para microservices em escala global, incluindo custos operacionais.
- Google Cloud — Monolith vs Microservices: When to Use Each — Guia do Google Cloud com matriz de decisão baseada em domínio, equipe e requisitos técnicos.