Microserviços: quando a complexidade vale a pena

1. A promessa e o custo dos microserviços

1.1. O que microserviços realmente prometem

Microserviços prometem escalabilidade independente, autonomia de equipes e resiliência isolada. A ideia é que cada serviço possa ser escalado horizontalmente conforme sua própria demanda, sem afetar os demais. Times podem desenvolver, testar e implantar seus serviços em ciclos independentes, usando tecnologias diferentes conforme a necessidade. Se um serviço falha, os demais continuam operando — em teoria.

1.2. O custo oculto

A realidade é que microserviços introduzem complexidade operacional significativa. Consistência distribuída exige padrões como sagas ou compensações manuais. Debugging em um sistema distribuído requer tracing distribuído, logs centralizados e métricas correlacionadas. A latência de rede entre serviços substitui chamadas locais no monolito. Toda chamada remota pode falhar — e você precisa lidar com timeouts, retries e circuit breakers.

1.3. A armadilha do hype vs. necessidade real

Muitas organizações adotam microserviços porque "todo mundo está fazendo", sem avaliar se a complexidade adicional se justifica. O resultado: sistemas distribuídos frágeis, times sobrecarregados e entregas mais lentas do que com um monolito bem estruturado.

2. Indicadores objetivos para adoção de microserviços

2.1. Limites de escala do monolito

Quando sua equipe cresce além de 15-20 desenvolvedores, ou seu deploy leva mais de 30 minutos, ou uma alteração simples impacta múltiplos módulos — você tem indicadores de que o monolito está no limite.

2.2. Fronteiras de domínio claras

Se seu sistema já possui limites naturais entre contextos delimitados (bounded contexts), como "catálogo de produtos" e "processamento de pedidos", com baixo acoplamento entre eles, a decomposição é mais segura.

2.3. Necessidade de tecnologias heterogêneas

Se diferentes partes do sistema exigem bancos de dados distintos (relacional, NoSQL, grafo) ou linguagens diferentes (Python para ML, Go para alta performance), microserviços permitem essa heterogeneidade sem comprometer a arquitetura.

3. Quando a complexidade NÃO vale a pena

3.1. Projetos verdes sem domínio maduro

Em projetos novos, o domínio ainda está sendo descoberto. Microserviços prematuras congelam fronteiras que podem estar erradas. Refatorar um monolito é mais barato que refatorar uma malha de serviços.

3.2. Equipes pequenas e sem cultura DevOps

Com menos de 5 desenvolvedores e sem experiência em orquestração de contêineres, CI/CD robusto e observabilidade, microserviços consomem mais tempo em infraestrutura do que em lógica de negócio.

3.3. Requisitos transacionais rígidos

Se seu sistema exige consistência ACID em múltiplas operações (ex.: transferência bancária), microserviços forçam padrões complexos de compensação. Monolitos com transações distribuídas locais são mais simples.

4. Estratégias de decomposição que reduzem o custo

4.1. Decomposição por domínio (DDD)

Use Domain-Driven Design para identificar contextos delimitados. Cada microserviço deve corresponder a um contexto de negócio, não a camadas técnicas (controller, service, repository separados em serviços diferentes).

# Exemplo: decomposição por domínio
# Serviço de Pedidos gerencia: pedido, item, pagamento
# Serviço de Catálogo gerencia: produto, categoria, estoque

4.2. Tamanho ideal do serviço

Evite nano-serviços (cada método vira um serviço). Prefira serviços que encapsulem um agregado DDD completo. Um serviço "gordo" com 3-5 entidades é mais fácil de manter que 20 micro-serviços anêmicos.

4.3. Comunicação: REST síncrono vs. mensageria assíncrona

Prefira comunicação assíncrona via filas (RabbitMQ, Kafka) para operações que não exigem resposta imediata. REST síncrono é aceitável para consultas, mas evite coreografia frágil com múltiplas chamadas encadeadas.

# Padrão assíncrono recomendado
# Serviço de Pedidos publica evento "PedidoCriado"
# Serviço de Estoque consome e atualiza estoque
# Serviço de Notificação consome e envia email

5. Gerenciamento de dados distribuídos sem caos

5.1. Sagas vs. transações XA

Sagas quebram uma transação longa em passos locais com compensações. Prefira coreografia (cada serviço publica eventos) a orquestração (um serviço central coordena).

# Saga coreografada para criar pedido:
# 1. Serviço de Pedidos cria pedido (status: pendente)
# 2. Publica evento "PedidoReservado"
# 3. Serviço de Pagamento tenta cobrar
# 4. Se sucesso: publica "PagamentoConfirmado"
# 5. Se falha: publica "PagamentoFalhou"
# 6. Serviço de Pedidos compensa: cancela pedido

5.2. CQRS e Event Sourcing

CQRS separa leitura e escrita. Event Sourcing persiste eventos em vez de estado atual. Úteis para sistemas que precisam de auditoria ou visões de leitura complexas.

5.3. Anti-padrões

Banco compartilhado entre serviços quebra o isolamento. Chamadas síncronas em cascata (A chama B que chama C) criam dependência de disponibilidade e latência.

6. Infraestrutura como pré-requisito

6.1. Service mesh e API gateways

Service mesh (Istio, Linkerd) gerencia comunicação entre serviços sem modificar código. API gateway (Kong, NGINX) centraliza autenticação, rate limiting e roteamento. Custo fixo mínimo: pelo menos 2 servidores para o mesh.

6.2. Observabilidade distribuída

Implemente tracing distribuído (Jaeger, Zipkin), métricas (Prometheus) e logs centralizados (ELK). Sem isso, diagnosticar falhas em um sistema com 10+ serviços é inviável.

6.3. CI/CD e testes de contrato

Automatize deploy com pipelines (GitLab CI, Jenkins). Use testes de contrato (Pact) para garantir que provedores e consumidores de APIs evoluam sem quebrar.

# Pipeline típico para microserviço:
# build → testes unitários → testes de contrato →
# build imagem Docker → deploy em staging →
# testes integração → deploy produção (canário)

7. Estratégia de migração: do monolito para microserviços

7.1. Padrão Strangler Fig

Extraia funcionalidades do monolito incrementalmente. Crie um novo serviço, redirecione tráfego gradualmente, remova o código antigo.

# Fases do Strangler Fig:
# 1. Identificar funcionalidade candidata
# 2. Criar novo serviço com mesma interface
# 3. Adicionar roteamento condicional (feature flag)
# 4. Migrar 10% do tráfego, monitorar, aumentar gradualmente
# 5. Remover código do monolito

7.2. Primeiro alvo: alto valor, baixo risco

Escolha módulos com baixo acoplamento ao resto do sistema e alto valor de negócio (ex.: notificações, relatórios). Evite módulos centrais como "autenticação" ou "processamento de pagamentos".

7.3. Métricas de sucesso

Monitore: tempo de deploy (de horas para minutos), MTTR (tempo médio de recuperação), frequência de falhas, e lead time para mudanças.

8. Conclusão: o balanço final

8.1. Checklist prático

  • [ ] Equipe com 15+ desenvolvedores?
  • [ ] Deploy do monolito leva mais de 30 minutos?
  • [ ] Fronteiras de domínio claras e estáveis?
  • [ ] Cultura DevOps e CI/CD madura?
  • [ ] Necessidade de escalabilidade independente?

Se respondeu "sim" a pelo menos 3, microserviços podem valer a pena.

8.2. O custo de não fazer

Monolitos também têm custos: deploy lento, equipes pisando nos pés umas das outras, escalabilidade desbalanceada. Avalie ambos os lados.

8.3. Microserviços como ferramenta

Microserviços não são o destino arquitetural. São uma ferramenta para resolver problemas específicos de escala organizacional e técnica. Use com critério, não por moda.

Referências