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