Microserviços não são bala de prata
Microserviços são uma ferramenta poderosa, mas não são a resposta para tudo. Antes de embarcar nessa arquitetura, é fundamental entender os trade-offs envolvidos.
A verdade incômoda: a maioria das aplicações funciona perfeitamente bem como um monólito bem estruturado. Microserviços adicionam complexidade operacional, latência de rede e desafios de consistência de dados que simplesmente não existem em um monólito.
Quando microserviços fazem sentido
Times grandes e independentes
Se você tem 5+ times trabalhando no mesmo código, as colisões e dependências entre equipes se tornam um gargalo. Microserviços permitem que cada time tenha ownership completo de seu domínio.
Necessidades de escala diferentes
Se o módulo de busca recebe 100x mais tráfego que o módulo de admin, escalar ambos juntos é desperdício. Com microserviços, você escala apenas o que precisa.
Domínio complexo e bem definido
Quando o negócio tem domínios claros (pedidos, pagamentos, inventário, notificações) que podem evoluir independentemente, microserviços alinham bem com a estrutura organizacional.
Domain-Driven Design como fundação
Microserviços sem DDD são serviços pequenos com problemas grandes. Domain-Driven Design fornece as ferramentas conceituais para definir os limites certos entre serviços.
Bounded Contexts
Cada microserviço deve corresponder a um bounded context — uma área do domínio com linguagem, regras e modelos próprios. O serviço de "Pedidos" tem sua própria definição de "cliente" (nome, endereço de entrega), diferente do serviço de "Marketing" (segmento, preferências, histórico de engajamento).
Quando dois serviços precisam compartilhar o mesmo modelo de dados, os limites estão errados. Redesenhe antes de implementar.
Context Mapping
Defina como os bounded contexts se relacionam: shared kernel, customer-supplier, conformist, anti-corruption layer. Cada tipo de relacionamento implica diferentes níveis de acoplamento e autonomia.
O anti-corruption layer é particularmente útil na migração: ele traduz entre o modelo do sistema legado e o modelo do microserviço novo, protegendo o serviço novo de inconsistências do legado.
Padrões essenciais
API Gateway
Ponto de entrada único para todos os clientes. Faz roteamento, autenticação, rate limiting e agregação de respostas. Evita que clientes precisem conhecer a topologia interna dos serviços.
Em implementações maduras, o API Gateway também faz: versionamento de API, transformação de payloads, caching de respostas e observabilidade centralizada. Kong, AWS API Gateway e Envoy são opções populares.
Event-Driven Communication
Em vez de chamadas síncronas entre serviços, publique eventos. Quando um pedido é criado, o serviço de pedidos publica um evento. Pagamento, estoque e notificação reagem independentemente. Isso reduz acoplamento e melhora resiliência.
Escolha entre coreografia (serviços reagem a eventos independentemente, sem coordenador central) e orquestração (um serviço central coordena o fluxo). Coreografia é mais desacoplada mas mais difícil de debugar. Orquestração é mais explícita mas cria um ponto central de falha.
Para o broker de mensagens, Kafka é ideal para alto throughput e replay de eventos. RabbitMQ é melhor para roteamento complexo e confirmação de entrega. SQS/SNS da AWS para simplicidade operacional.
Circuit Breaker
Quando um serviço downstream está lento ou fora, o circuit breaker abre e retorna uma resposta fallback em vez de ficar esperando e cascatear a falha para todo o sistema.
O padrão tem três estados: fechado (tráfego normal), aberto (bloqueia chamadas, retorna fallback) e meio-aberto (permite chamadas limitadas para testar recuperação). Bibliotecas como Resilience4j (Java), Polly (.NET) e opossum (Node.js) implementam isso de forma simples.
Combine circuit breaker com retry com backoff exponencial e timeout agressivo. Um timeout de 30 segundos pode parecer "seguro", mas em um request que atravessa 5 serviços, 30 segundos em cada nível significa 2.5 minutos de espera para o usuário.
Saga Pattern
Para operações que envolvem múltiplos serviços, o Saga Pattern coordena uma sequência de transações locais com compensações definidas para cada passo. Se o passo 3 falha, passos 2 e 1 são compensados automaticamente.
Exemplo prático: criar pedido (passo 1), reservar estoque (passo 2), processar pagamento (passo 3). Se o pagamento falha, a compensação libera o estoque reservado e cancela o pedido.
CQRS (Command Query Responsibility Segregation)
Separe modelos de leitura e escrita. O modelo de escrita otimiza para consistência e validação. O modelo de leitura otimiza para performance de queries. Em muitos domínios, o padrão de leitura é radicalmente diferente do padrão de escrita.
CQRS combina naturalmente com Event Sourcing: eventos são a fonte de verdade (escrita), e projeções otimizadas atendem as queries (leitura). Isso permite reconstruir o estado a qualquer momento e criar novas projeções retroativamente.
Gestão de dados distribuídos
Database per Service
Cada microserviço deve ter seu próprio banco de dados. Compartilhar banco entre serviços cria acoplamento que anula os benefícios da arquitetura. Se dois serviços precisam dos mesmos dados, eles se comunicam via API ou eventos — nunca via banco compartilhado.
Consistência eventual
Em microserviços, consistência forte entre serviços é impraticável. Aceite consistência eventual: quando um pedido é criado, o serviço de estoque pode levar milissegundos (ou até segundos) para refletir a reserva.
Projete a experiência do usuário para acomodar isso: mostre "processando" em vez de "confirmado" até que todos os serviços tenham convergido.
Outbox Pattern
Para garantir que uma operação no banco e a publicação de um evento aconteçam atomicamente, use o Outbox Pattern: escreva o evento em uma tabela "outbox" na mesma transação do banco. Um processo separado lê a tabela e publica os eventos no broker.
Isso resolve o problema de "atualizei o banco mas o evento não foi publicado" (ou vice-versa), que é surpreendentemente comum e difícil de debugar.
Observabilidade é essencial
Em um monólito, debugar é relativamente simples: stack trace, logs locais, breakpoints. Em microserviços, um request atravessa múltiplos serviços e a causa raiz pode estar em qualquer um deles.
Invista desde o início em:
- Distributed tracing (OpenTelemetry) para rastrear requests end-to-end. Cada request recebe um trace ID que é propagado entre todos os serviços
- Centralização de logs com correlation IDs vinculados ao trace. Quando algo falha, você precisa ver os logs de todos os serviços envolvidos em um único lugar
- Métricas RED por serviço (Rate, Errors, Duration) com alertas automáticos quando thresholds são ultrapassados
- Service mesh (Istio, Linkerd) para observabilidade de rede, mutual TLS, traffic splitting e fault injection para testes de resiliência
- Dependency maps automáticos que mostram como serviços se conectam e onde estão os gargalos
Testes em microserviços
Testar microserviços exige estratégias diferentes de testar um monólito.
Testes unitários continuam essenciais dentro de cada serviço. Testes de contrato (Pact, Spring Cloud Contract) validam que a interface entre serviços não quebrou — sem precisar rodar todos os serviços juntos. Testes de integração com containers (Testcontainers) validam comunicação com banco, cache e message broker.
Testes end-to-end são caros e frágeis em microserviços. Minimize-os e invista mais em testes de contrato e observabilidade em produção (chaos engineering, canary deploys).
Migre incrementalmente
Não tente extrair todos os microserviços de uma vez. Use o Strangler Fig Pattern: identifique o domínio com mais dor, extraia como microserviço, estabilize e repita.
Um roteiro típico de migração:
- Identifique o bounded context com mais mudanças frequentes ou necessidade de escala independente
- Crie o microserviço com seu próprio banco e API
- Implemente o anti-corruption layer para traduzir entre monólito e serviço novo
- Redirecione tráfego gradualmente do monólito para o serviço novo
- Remova o código do monólito somente após estabilizar
- Repita para o próximo domínio
A jornada do monólito para microserviços é uma maratona, não um sprint. Cada serviço extraído deve justificar sua existência com benefícios concretos: deploy independente, escala otimizada ou produtividade de time. Se não justifica, mantenha no monólito.
