Índice
- O que são microserviços?
- Quando (não) usar microserviços
- Padrões arquiteturais
- Comunicação entre serviços
- Dados em microserviços
- Deploy e orquestração
- Observabilidade
- Escala real: números
- Erros comuns
- Caso real: fintech que escalou de 5K a 2M usuários
O que são microserviços? {#o-que-sao}
Microserviços são uma forma de organizar um sistema de software em que, em vez de construir uma única aplicação grande (chamada monolito), você divide em vários serviços pequenos e independentes, cada um responsável por uma área específica do negócio.
Analogia: Imagine uma cozinha de restaurante. Um monolito é como um cozinheiro que faz tudo: prepara a entrada, o prato principal, a sobremesa, lava a louça, atende o garçom. Se ele fica doente, o restaurante inteiro para.
Microserviços são como uma cozinha com estações separadas: uma para entradas, uma para grelhados, uma para sobremesas, uma para lavar louça. Cada estação trabalha independentemente. Se a estação de sobremesas quebra, o restaurante ainda serve entradas e pratos principais.
Cada microserviço:
- Tem seu próprio banco de dados (não compartilha com outros)
- Pode ser deployado (colocado em produção) independentemente
- Pode ser escrito em linguagens diferentes (um em Python, outro em Go)
- Pode escalar (crescer para atender mais usuários) independentemente
- Comunica-se com outros serviços via rede (API, eventos)
Monolito vs Microserviços — visão geral
| Aspecto | Monolito | Microserviços |
|---|---|---|
| Estrutura | Uma aplicação, um código | Vários serviços independentes |
| Banco de dados | Um banco compartilhado | Cada serviço tem o seu |
| Deploy | Todo o sistema de uma vez | Cada serviço separadamente |
| Escala | Tudo escala junto | Cada serviço escala na medida |
| Complexidade | Baixa no início, alta depois | Alta desde o início |
| Time | Funciona com time pequeno | Precisa de times maiores |
| Manutenção | Mudanças afetam tudo | Mudanças isoladas por serviço |
Quando (não) usar microserviços {#quando}
Microserviços não são bala de prata (não resolvem todos os problemas). Na verdade, para muitos projetos, um monolito é a escolha certa.
Use microserviços quando:
- Time > 8 pessoas: a complexidade operacional só se justifica com um time grande o suficiente para manter múltiplos serviços
- Domínios distintos: e-commerce, pagamentos e logística têm ciclos de desenvolvimento e necessidades diferentes. Faz sentido separá-los.
- Escala diferenciada: o catálogo de produtos recebe 100x mais tráfego que o checkout. Separando, você escala só o catálogo.
- Deploy independente: times precisam liberar atualizações sem coordenar com outros times
- Requisitos técnicos diferentes: o serviço de busca precisa de Elasticsearch, o de pagamentos precisa de PostgreSQL transacional
NÃO use microserviços quando:
- Time < 5 pessoas: monolito modular (um código organizado em módulos bem separados) é mais produtivo e simples de manter
- MVP / fase inicial: valide o produto antes de otimizar a arquitetura. "Primeiro faça funcionar, depois faça rápido, depois faça bonito."
- "Porque o Netflix usa": você não é o Netflix (ainda). O Netflix tem milhares de engenheiros e bilhões de requisições. Sua startup com 500 usuários não precisa da mesma arquitetura.
Regra de ouro: Comece com monolito modular (um código só, mas organizado em módulos bem separados). Extraia microserviços apenas quando um domínio específico tiver requisitos de escala, deploy ou time que justifiquem a separação.
Erro comum: O CTO lê um artigo sobre "Netflix migrou para microserviços" e decide migrar a startup com 3 desenvolvedores e 200 usuários. Resultado: 6 meses migrando, nenhuma feature nova lançada, e o sistema ficou mais lento e mais difícil de manter.
Padrões arquiteturais {#padroes}
API Gateway
Analogia: O API Gateway é como a recepção de um prédio comercial. Todos os visitantes entram pela recepção. A recepcionista verifica a identidade (autenticação), diz para qual sala ir (roteamento), limita quantas pessoas entram por hora (rate limiting) e pode dar informações de múltiplos andares em uma única visita (agregação).
O API Gateway é o ponto único de entrada para todos os clientes. Ele é responsável por:
- Roteamento: cada request vai para o serviço certo
(
GET /pedidos→ serviço de pedidos,GET /produtos→ serviço de catálogo) - Autenticação/Autorização: verifica quem é o usuário e o que pode fazer
- Rate limiting: limita quantas requisições um cliente pode fazer por minuto
- Transformação de request/response: converte formatos se necessário
- Agregação de múltiplos serviços: combina respostas de vários serviços em uma única resposta para o cliente
[Cliente] → [API Gateway] → [Serviço de Pedidos]
→ [Serviço de Catálogo]
→ [Serviço de Pagamentos]
Ferramentas populares: Kong, AWS API Gateway, Traefik, Envoy
Service Mesh
Analogia: Service Mesh é como um sistema de rádio interno do prédio. Cada andar (serviço) se comunica com outros andares pelo rádio, sem precisar saber o número da sala. O sistema de rádio garante que a comunicação é segura (criptografada), rastreável (gravada) e resiliente (se uma sala não responde, tenta de novo automaticamente).
Service Mesh lida com a comunicação service-to-service (entre serviços, não entre cliente e serviço):
- mTLS (mutual TLS): criptografia em trânsito entre serviços
- Retry/circuit breaker: se um serviço falha, tenta de novo; se continua falhando, "desarma o disjuntor" e para de tentar (explico abaixo)
- Distributed tracing: rastreia uma requisição através de múltiplos serviços
- Traffic splitting: divide tráfego entre versões (ex: 95% v1, 5% v2 para testar nova versão — chamado canary deploy)
Ferramentas: Istio, Linkerd, Consul Connect
Backend for Frontend (BFF)
Analogia: Em vez de uma recepção única para todos (API Gateway padrão), o BFF é como ter recepções especializadas: uma para visitantes, uma para entregadores, uma para fornecedores. Cada uma atende as necessidades específicas de cada tipo de visitante.
Um BFF (Backend for Frontend) é um gateway especializado por tipo de cliente:
- Web BFF: agrega dados para o dashboard completo do navegador
- Mobile BFF: responde só o essencial (menos dados = menos consumo de bateria e dados móveis)
- Partner BFF: expõe apenas endpoints públicos para parceiros/integradores
Saga Pattern
Analogia: Saga é como uma corrida de revezamento. Em vez de uma única transação que faz tudo (o que seria impossível entre serviços diferentes), cada serviço faz sua parte e passa o bastão para o próximo. Se um corredor tropeça, os anteriores precisam "desfazer" o que fizeram (compensação).
Saga é o padrão para transações distribuídas — quando uma operação precisa mudar dados em múltiplos serviços e não há como fazer tudo em uma transação única (ACID).
Por que não transação ACID?: ACID (Atomicidade, Consistência, Isolamento, Durabilidade) garante que tudo acontece ou nada acontece. Mas quando os dados estão em bancos diferentes (cada microserviço tem o seu), não dá para fazer uma transação que englobe todos. Saga resolve isso com compensação.
Dois estilos de Saga:
- Choreography (coreografia): cada serviço publica um evento, o próximo serviço reage automaticamente. Sem orquestrador central.
- Orchestration (orquestração): um orquestrador coordena a sequência, chamando cada serviço na ordem certa.
[Pedido criado] → [Estoque reservado] → [Pagamento processado] → [Pedido confirmado]
↓ falha no pagamento
[Estoque liberado] ← (compensação: desfaz a reserva)
Analogia da compensação: Você comprou um produto online. O sistema reservou o estoque, processou o pagamento, mas o pagamento foi recusado. A Saga desfaz: libera o estoque que estava reservado. É como devolver um item à prateleira quando o cartão é recusado no caixa.
Comunicação entre serviços {#comunicacao}
Síncrona (REST/gRPC)
Analogia: Comunicação síncrona é como uma ligação telefônica. Você liga, espera a pessoa atender, pergunta, espera a resposta, e só depois desliga. Se a pessoa não atende, você fica esperando.
- Use para: consultas que precisam de resposta imediata (ex: "este produto está em estoque?")
- Problema: acoplamento temporal — se o serviço B cai, o serviço A também "cai" (fica esperando resposta que nunca vem)
- Mitigação: circuit breaker (disjuntor) — se B falha N vezes seguidas, A para de tentar e responde com um fallback (resposta degradada) em vez de ficar esperando
O que é Circuit Breaker?: Como o disjuntor da sua casa. Se há um curto-circuito (serviço B falhando), o disjuntor "desarma" e corta a energia (para de chamar B). Após um tempo, tenta "religar" (half-open) para ver se B voltou. Se sim, volta ao normal. Se não, desarma de novo. Ferramentas: Resilience4j (Java), Polly (.NET), opossum (Node.js).
Assíncrona (Eventos/Mensagens)
Analogia: Comunicação assíncrona é como mandar um e-mail. Você escreve, envia e continua sua vida. Não espera a pessoa responder para continuar trabalhando. Quando ela responder, você lê.
- Use para: comandos que podem ser processados depois (ex: "envie e-mail de confirmação", "recalcule o score do cliente")
- Ferramentas: Kafka, RabbitMQ, NATS, Amazon SQS
- Vantagem: desacoplamento temporal — o produtor não depende do consumidor estar online
# Padrão: evento publicado, múltiplos serviços consomem independentemente
order_created = {
"event_id": "uuid-123", # ID único do evento
"event_type": "order.created", # tipo do evento
"data": {
"order_id": 456,
"customer_id": 789,
"total": 199.90,
"items": [{"product_id": 1, "qty": 2}]
},
"timestamp": "2026-08-26T10:00:00Z"
}
# Publica no Kafka/RabbitMQ — não espera ninguém processar
producer.send("order-events", order_created)
# Múltiplos serviços consomem independentemente:
# - Serviço de estoque consome → reserva os itens
# - Serviço de notificação consome → envia e-mail de confirmação
# - Serviço de analytics consome → atualiza dashboard de vendas
# Cada um faz sua parte sem depender dos outros
gRPC para service-to-service
Analogia: gRPC é como dois cozinheiros se comunicando por um sistema de pedidos digital em vez de gritar pela cozinha. Mais rápido, mais organizado, e com um "menu" definido (contrato) que ambos seguem.
gRPC é uma alternativa ao REST para comunicação entre serviços:
- Binário (Protocol Buffers): mensagens 3-10x menores que JSON → menos banda, mais rápido
- HTTP/2: suporta multiplexação (múltiplas mensagens em uma conexão) e streaming
- Contrato tipado (.proto): um arquivo define exatamente os tipos de dados → geração automática de código para cliente e servidor
// pedido.proto — contrato que define a comunicação
syntax = "proto3";
service PedidoService {
rpc CriarPedido (CriarPedidoRequest) returns (PedidoResponse);
rpc GetPedido (GetPedidoRequest) returns (PedidoResponse);
}
message CriarPedidoRequest {
int32 cliente_id = 1;
repeated ItemPedido itens = 2;
}
message ItemPedido {
int32 produto_id = 1;
int32 quantidade = 2;
}
message PedidoResponse {
int32 id = 1;
string status = 2;
double total = 3;
}
Quando usar gRPC vs REST: REST para APIs externas (clientes, parceiros), gRPC para comunicação interna entre serviços (onde performance importa e você controla ambos os lados).
Dados em microserviços {#dados}
Princípio fundamental: cada microserviço é dono do seu banco de dados. Nenhum serviço acessa o banco de outro diretamente.
Analogia: Em uma empresa, o setor financeiro tem seus arquivos, o RH tem os seus. O financeiro não vai mexer nos arquivos do RH diretamente — se precisa de informação, pede ao RH. Em microserviços é igual: se o serviço A precisa de dados do serviço B, ele pede via API/eventos, não acessa o banco do B diretamente.
Por que?: Se dois serviços compartilham o banco, uma mudança na tabela de um afeta o outro. Você perde o isolamento — a principal vantagem dos microserviços.
Padrões de dados
| Padrão | Quando usar | Trade-off |
|---|---|---|
| Database per service | Padrão default (cada serviço tem seu banco) | Isolamento total, mas consultas que cruzam serviços são complexas |
| Shared database | Transição gradual de monolito | Rápido para migrar, mas quebra o isolamento |
| CQRS (Command Query Responsibility Segregation) | Leitura e escrita têm necessidades diferentes | Otimiza cada lado, mas adiciona complexidade |
| Event Sourcing | Precisa de auditoria completa de todas as mudanças | Permite "replay" da história, mas o storage cresce indefinidamente |
| Saga | Transação que envolve múltiplos serviços | Consistência eventual (não imediata), exige compensação |
O que é CQRS?: Separa operações de escrita (Command — criar, atualizar, deletar) de leitura (Query — buscar, listar). Em vez de um único modelo para ambos, você tem um modelo otimizado para escrita (normalizado, transacional) e outro otimizado para leitura (desnormalizado, rápido para consultas).
O que é Event Sourcing?: Em vez de guardar apenas o estado atual ("saldo = R$ 1.000"), guarda todos os eventos que levaram a esse estado ("+500 depósito, -200 saque, +700 depósito"). Permite reconstruir o estado a qualquer momento e auditar toda a história. Mas o armazenamento cresce para sempre.
Sincronização de dados
Quando um serviço precisa de dados de outro, há 3 abordagens:
- API call (chamada direta): simples, mas cria acoplamento temporal (se o serviço B cai, A não consegue obter os dados)
- Réplica de leitura: copia dados via CDC (Change Data Capture — captura mudanças do banco de B e replica para uma cópia de leitura de A) → rápido, mas os dados são eventualmente consistentes (podem estar alguns segundos desatualizados)
- Materialized view (visão materializada): agregação pré-computada → rápido para consultas complexas, mas precisa ser atualizada periodicamente
Deploy e orquestração {#deploy}
Kubernetes: o padrão de facto
Analogia: Kubernetes (frequentemente abreviado como K8s) é como um gerente de fábrica. Você diz "quero 3 instâncias do serviço de pedidos rodando, cada uma usando no máximo 512MB de memória". O Kubernetes garante que isso aconteça: se uma instância cai, ele cria outra. Se o tráfego aumenta, ele cria mais instâncias. Se diminui, ele remove. Tudo automaticamente.
Kubernetes é o padrão da indústria para orquestrar contêineres (aplicações empacotadas com tudo que precisam para rodar). Veja um exemplo de configuração:
# deployment.yaml — Define como o serviço de pedidos deve rodar
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3 # sempre 3 instâncias rodando (alta disponibilidade)
selector:
matchLabels:
app: order-service
template:
spec:
containers:
- name: order-service
image: registry.com/order-service:v1.4.2 # versão da imagem
resources:
# 'requests' = mínimo garantido (K8s reserva isto)
requests:
memory: "256Mi" # 256 MB de memória
cpu: "250m" # 0.25 CPU (1 CPU = 1000m)
# 'limits' = máximo permitido (K8s limita a isto)
limits:
memory: "512Mi" # até 512 MB
cpu: "500m" # até 0.5 CPU
# Liveness: "este serviço está vivo?" — se falhar, K8s reinicia
livenessProbe:
httpGet:
path: /health # endpoint que retorna 200 se saudável
port: 8080
# Readiness: "este serviço está pronto para receber tráfego?"
# — se falhar, K8s tira do load balancer mas não reinicia
readinessProbe:
httpGet:
path: /ready # endpoint que retorna 200 se pronto
port: 8080
Diferença entre Liveness e Readiness: Liveness verifica se a aplicação está rodando (se falhar, reinicia). Readiness verifica se está pronta para receber requisições (ex: terminou de carregar configurações, conectou ao banco). Se readiness falhar, o serviço fica fora do balanceador de carga mas não é reiniciado.
Estratégias de deploy
| Estratégia | Como funciona | Quando usar |
|---|---|---|
| Rolling | Substitui instâncias gradualmente (1 por vez) | Default, sem downtime |
| Blue/Green | Dois ambientes idênticos; switch de tráfego instantâneo | Validação rápida com rollback fácil |
| Canary | 5% → 25% → 50% → 100% do tráfego na nova versão | Risco de regressão alto |
| Feature flags | Liga/desliga funcionalidade sem novo deploy | Experimentação A/B, rollout controlado |
Analogia Blue/Green: É como ter dois palcos de teatro idênticos. A peça rola no palco Azul. Você prepara a nova versão no palco Verde. Quando tudo está pronto, muda a plateia do Azul para o Verde instantaneamente. Se algo der errado, muda de volta num segundo.
Analogia Canary: É como enviar uma nova receita para 5% dos clientes primeiro. Se ninguém reclama, envia para 25%. Se alguém reclama, para. Se está tudo bem, continua até 100%. O nome vem dos canários que mineiros levavam para dentro das minas — se o canário morria, era sinal de gás tóxico e todos evacuavam.
Auto-scaling
# hpa.yaml — Horizontal Pod Autoscaler: escala automaticamente
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
kind: Deployment
name: order-service
minReplicas: 3 # mínimo de 3 instâncias (sempre)
maxReplicas: 50 # máximo de 50 instâncias
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # se CPU médio > 70%, cria mais instâncias
Como funciona: O Kubernetes monitora o uso de CPU. Se a média passa de 70%, ele cria mais instâncias para dividir a carga. Se cai, ele remove instâncias para economizar. Tudo automático, 24/7.
Observabilidade {#observabilidade}
Observabilidade é a capacidade de entender o estado interno do sistema observando suas saídas (logs, métricas, traces). Sem ela, gerenciar microserviços é como dirigir de olhos vendados.
Analogia: Observabilidade é o painel do carro + GPS + câmera de ré. Logs são o histórico de manutenção. Métricas são o painel (velocímetro, temperatura, combustível). Traces são o GPS que mostra o caminho completo de cada viagem.
Os três pilares:
1. Logs estruturados
{
"timestamp": "2026-08-26T10:30:00.123Z",
"level": "info",
"service": "order-service",
"trace_id": "abc123", # ID que rastreia a requisição em todos os serviços
"span_id": "def456", # ID deste trecho específico
"message": "Order created",
"order_id": 456,
"customer_id": 789,
"latency_ms": 142
}
Stack: Fluentd/Fluent Bit (coleta) → Elasticsearch/Loki (armazenamento) → Kibana/Grafana (visualização)
Por que estruturado?: Logs em texto livre ("Order 456 created in 142ms") são difíceis de buscar e filtrar. Logs em JSON permitem queries como "mostre todos os logs onde service=order-service AND latency_ms > 500".
2. Métricas (Prometheus + Grafana)
- Prometheus coleta métricas de cada serviço periodicamente
- Grafana visualiza dashboards em tempo real
- Alertas: erro rate, latência P99, saturação de recursos
Métricas essenciais (frameworks USE e RED):
- RED (por serviço): Rate (taxa de requisições), Errors (taxa de erro), Duration (latência)
- USE (por recurso): Utilization (utilização), Saturation (saturação), Errors (erros)
3. Distributed tracing
Analogia: Distributed tracing é como o rastreamento de encomenda dos correios. Você vê cada passo: saiu do centro de distribuição → chegou na unidade de triagem → saiu para entrega → entregue. Com timestamps em cada etapa, você vê onde demorou mais.
OpenTelemetry instrumenta cada serviço. Jaeger/Tempo visualiza o trace completo de uma requisição:
[Gateway] → [Auth] → [Order] → [Inventory] → [Payment]
2ms 15ms 45ms 30ms 50ms
Total: 142ms
Por que isso importa?: Um cliente reclama "a compra demorou 10 segundos". Sem distributed tracing, você não sabe qual dos 5 serviços é o culpado. Com tracing, você vê imediatamente: o serviço de Inventory demorou 8 segundos.
Escala real: números {#numeros}
| Volume | Arquitetura recomendada | Infraestrutura |
|---|---|---|
| 1K req/dia | Monolito | 1 instância (t3.small) |
| 10K req/dia | Monolito modular | 2 instâncias + Load Balancer |
| 100K req/dia | 2-3 serviços | 3-5 pods + Redis cache |
| 1M req/dia | 5-10 microserviços | K8s + auto-scaling + Kafka |
| 10M req/dia | 15-30 microserviços | K8s multi-AZ + CDN + event-driven |
Analogia: Você não constrói um shopping center para uma banca de jornal. Cada volume de tráfego pede uma arquitetura diferente. Comece pequeno e evolua conforme o negócio cresce.
Custos aproximados (AWS, 2026)
| Fase | Tráfego | Custo/mês |
|---|---|---|
| MVP | 1K req/dia | $20-50 |
| Crescimento | 100K req/dia | $200-500 |
| Escala | 1M req/dia | $1.000-3.000 |
| Alta escala | 10M req/dia | $5.000-15.000 |
Dica: Otimize custos com:
- Auto-scaling agressivo: reduz instâncias quando o tráfego cai (à noite, fins de semana)
- Spot Instances: instâncias mais baratas (até 90% de desconto) para workloads que toleram interrupção (processamento em batch, por exemplo)
- CDN (CloudFront): reduz carga na origem servindo conteúdo estático pela borda (mais próximo do usuário)
Erros comuns {#erros}
Erro 1: Começar com microserviços no MVP
O que acontece: A startup decide começar já com microserviços "para não precisar migrar depois". 3 desenvolvedores gastam 4 meses configurando Kubernetes, service mesh e CI/CD antes de ter um único usuário.
O que NÃO fazer:
❌ Dia 1 da startup, 0 usuários, 0 receita:
- 8 microserviços
- Kubernetes com Istio
- Kafka para eventos
- CI/CD com ArgoCD
- Observabilidade com 5 ferramentas
Resultado: 4 meses de setup, nenhuma feature para o usuário, dinheiro acabando.
O que fazer:
✅ Dia 1 da startup:
- 1 monolito modular (Node.js ou Python)
- 1 banco de dados (PostgreSQL)
- 1 instância (Railway ou Fly.io)
- Deploy com git push
Quando tiver 100K+ req/dia e 8+ desenvolvedores:
- Aí sim, extraia microserviços conforme a necessidade
Erro 2: Compartilhar banco de dados entre serviços
O que acontece: Dois microserviços acessam o mesmo banco "para facilitar joins". Uma mudança na tabela de um serviço quebra o outro. Os serviços não são realmente independentes.
O que NÃO fazer:
# ❌ ERRADO: serviço de pedidos acessa o banco de clientes diretamente
def get_pedido_com_cliente(pedido_id):
pedido = db_pedidos.query("SELECT * FROM pedidos WHERE id = ?", pedido_id)
# Acessando diretamente a tabela de clientes — quebra o isolamento!
cliente = db_pedidos.query("SELECT * FROM clientes WHERE id = ?", pedido.cliente_id)
return {**pedido, "cliente": cliente}
O que fazer:
# ✅ CERTO: serviço de pedidos pede dados de cliente via API do serviço de clientes
def get_pedido_com_cliente(pedido_id):
pedido = db_pedidos.query("SELECT * FROM pedidos WHERE id = ?", pedido_id)
# Pede ao serviço de clientes via API — mantém o isolamento
cliente = cliente_service_api.get(pedido.cliente_id)
return {**pedido, "cliente": cliente}
Erro 3: Comunicação síncrona em cascata
O que acontece: O serviço A chama B, que chama C, que chama D. Se D cai, C fica esperando, B fica esperando, A fica esperando, e o cliente recebe timeout. Um único serviço derruba toda a cadeia.
O que NÃO fazer:
❌ Cadeia síncrona:
[Cliente] → [A] → [B] → [C] → [D (caiu)]
← timeout ← timeout ← timeout ← timeout
Todos os serviços ficam presos esperando D.
O que fazer:
✅ Usar circuit breaker + comunicação assíncrona onde possível:
[Cliente] → [A] → [B] → [C] → [D (caiu)]
← circuit breaker "desarma" ← fallback imediato
A responde com dados parciais ou mensagem de "tente mais tarde".
Ou melhor: [A] publica evento, [B], [C], [D] consomem independentemente.
Nenhum depende do outro estar online.
Erro 4: Sem distributed tracing
O que acontece: Em produção, um cliente reclama que "às vezes o checkout demora 15 segundos". Você tem 6 serviços. Sem distributed tracing, você não faz ideia de qual serviço está lento. Fica testando um por um, por dias.
O que fazer: Implemente OpenTelemetry desde o início. O custo de implementar é baixo (algumas linhas de configuração por serviço) e o benefício é enorme quando há problemas em produção.
# Exemplo: instrumentação com OpenTelemetry em Python
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
tracer = trace.get_tracer(__name__)
@app.get("/pedidos/{pedido_id}")
async def get_pedido(pedido_id: int):
# Cria um span (trecho do trace) automaticamente
with tracer.start_as_current_span("get_pedido") as span:
span.set_attribute("pedido_id", pedido_id)
pedido = buscar_pedido(pedido_id) # sub-span automático
cliente = buscar_cliente(pedido.cliente_id) # sub-span automático
span.set_attribute("cliente_id", pedido.cliente_id)
return {**pedido, "cliente": cliente}
Erro 5: Ignorar consistência eventual
O que acontece: O serviço de pedidos confirma um pedido, mas o serviço de estoque ainda não processou a reserva (fila com atraso). O cliente vê "pedido confirmado" mas o estoque não foi reservado. Resultado: venda sem estoque.
O que NÃO fazer:
# ❌ ERRADO: assumir que tudo é imediato
def criar_pedido(dados):
pedido = salvar_pedido(dados)
publicar_evento_pedido_criado(pedido)
return {"status": "confirmado"} # afirma confirmação imediata
# Mas estoque ainda pode não ter reservado!
O que fazer:
# ✅ CERTO: usar Saga e informar status correto
def criar_pedido(dados):
pedido = salvar_pedido(dados, status="processando") # não confirmado ainda
publicar_evento_pedido_criado(pedido)
return {"status": "processando", "pedido_id": pedido.id}
# Cliente sabe que está em processamento
# Quando estoque reserva e pagamento confirma:
def on_estoque_reservado(evento):
if evento.sucesso:
atualizar_pedido_status(evento.pedido_id, "confirmado")
notificar_cliente(evento.pedido_id, "Pedido confirmado!")
else:
atualizar_pedido_status(evento.pedido_id, "cancelado_sem_estoque")
notificar_cliente(evento.pedido_id, "Produto esgotado, pedido cancelado")
Erro 6: Granularidade errada (serviços pequenos demais ou grandes demais)
O que acontece:
- Pequenos demais: cada endpoint vira um microserviço ("serviço de criar pedido", "serviço de listar pedido", "serviço de cancelar pedido"). 50 serviços para o que deveria ser 1. A comunicação entre eles é mais complexa que a lógica de negócio.
- Grandes demais: um "microserviço" que faz tudo do e-commerce (catálogo, carrinho, checkout, pagamentos, envio). É um monolito disfarçado.
Como acertar a granularidade: Agrupe por domínio de negócio (conceito de Domain-Driven Design). "Pedidos" é um domínio. "Catálogo" é outro. "Pagamentos" é outro. Dentro de cada, pode haver múltiplas operações, mas pertencem ao mesmo serviço.
Caso real: fintech que escalou de 5K a 2M usuários {#caso-real}
Uma fintech de pagamentos começou como monolito em Ruby on Rails com 5K usuários. Conforme cresceu, sentiu a dor:
- 50K usuários: deploys de 30 min (o monolito era grande), times bloqueando uns aos outros
- 200K usuários: o serviço de consulta de saldo sobrecarregava todo o sistema (era a operação mais frequente)
- 500K usuários: time de 20 desenvolvedores, mudanças em uma área quebravam outras
Migração gradual (8 meses):
- Extraíram primeiro o serviço de consulta de saldo (mais trafegado) → reduziu 60% da carga do monolito
- Extraíram o serviço de notificações → parou de travar o processamento principal quando o SES (e-mail) tinha lentidão
- Extraíram o serviço de anti-fraude → time dedicado, deploy independente, iteração rápida em regras de risco
- Migraram para Kubernetes com auto-scaling → sobreviveram a picos de Black Friday sem intervenção manual
Resultado aos 2M usuários:
- 12 microserviços (não 50 — granularidade por domínio)
- Deploy de cada serviço: 3-5 minutos
- Times independentes (4 squads de 5 pessoas)
- Latência P99: 200ms (antes: 2-5s nos picos)
- Custo de infra: $8.000/mês (antes: $12.000/mês com monolito superdimensionado)
- Zero downtime em deploys
A lição: Não migre tudo de uma vez. Extraia o serviço que mais dói primeiro (o de maior tráfego ou maior conflito entre times). Valide. Depois extraia o próximo. Migração incremental reduz risco e entrega valor desde o primeiro serviço extraído.
Desenhando sistemas escaláveis? A Inicialize Tec arquiteta e implementa microserviços, desde a extração do monolito até o deploy em Kubernetes com observabilidade completa. Conversamos sobre seu caso.