Índice

  1. O que são microserviços?
  2. Quando (não) usar microserviços
  3. Padrões arquiteturais
  4. Comunicação entre serviços
  5. Dados em microserviços
  6. Deploy e orquestração
  7. Observabilidade
  8. Escala real: números
  9. Erros comuns
  10. 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

AspectoMonolitoMicroserviços
EstruturaUma aplicação, um códigoVários serviços independentes
Banco de dadosUm banco compartilhadoCada serviço tem o seu
DeployTodo o sistema de uma vezCada serviço separadamente
EscalaTudo escala juntoCada serviço escala na medida
ComplexidadeBaixa no início, alta depoisAlta desde o início
TimeFunciona com time pequenoPrecisa de times maiores
ManutençãoMudanças afetam tudoMudanç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ãoQuando usarTrade-off
Database per servicePadrão default (cada serviço tem seu banco)Isolamento total, mas consultas que cruzam serviços são complexas
Shared databaseTransição gradual de monolitoRápido para migrar, mas quebra o isolamento
CQRS (Command Query Responsibility Segregation)Leitura e escrita têm necessidades diferentesOtimiza cada lado, mas adiciona complexidade
Event SourcingPrecisa de auditoria completa de todas as mudançasPermite "replay" da história, mas o storage cresce indefinidamente
SagaTransação que envolve múltiplos serviçosConsistê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:

  1. API call (chamada direta): simples, mas cria acoplamento temporal (se o serviço B cai, A não consegue obter os dados)
  2. 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)
  3. 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égiaComo funcionaQuando usar
RollingSubstitui instâncias gradualmente (1 por vez)Default, sem downtime
Blue/GreenDois ambientes idênticos; switch de tráfego instantâneoValidação rápida com rollback fácil
Canary5% → 25% → 50% → 100% do tráfego na nova versãoRisco de regressão alto
Feature flagsLiga/desliga funcionalidade sem novo deployExperimentaçã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}

VolumeArquitetura recomendadaInfraestrutura
1K req/diaMonolito1 instância (t3.small)
10K req/diaMonolito modular2 instâncias + Load Balancer
100K req/dia2-3 serviços3-5 pods + Redis cache
1M req/dia5-10 microserviçosK8s + auto-scaling + Kafka
10M req/dia15-30 microserviçosK8s 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)

FaseTráfegoCusto/mês
MVP1K req/dia$20-50
Crescimento100K req/dia$200-500
Escala1M req/dia$1.000-3.000
Alta escala10M 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):

  1. Extraíram primeiro o serviço de consulta de saldo (mais trafegado) → reduziu 60% da carga do monolito
  2. Extraíram o serviço de notificações → parou de travar o processamento principal quando o SES (e-mail) tinha lentidão
  3. Extraíram o serviço de anti-fraude → time dedicado, deploy independente, iteração rápida em regras de risco
  4. 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.