Construindo Microserviços Escaláveis: Patterns Reais das Trincheiras
Engenharia Arquitetura 15 de dezembro de 2025

Construindo Microserviços Escaláveis: Patterns Reais das Trincheiras

Um olhar prático sobre arquitetura de microserviços - os patterns que realmente funcionam, as armadilhas que nos pegaram, e porque às vezes um monólito não é tão ruim assim.

E
Engineering Team
Senior Solutions Architects
14 min de leitura

O Problema Real

Quando olhamos pela primeira vez para esta plataforma de serviços financeiros, ela tinha aquele cheiro familiar. Um monólito servindo 50.000 usuários diários, deployments que exigiam janelas de manutenção noturnas, e um código onde tocar no módulo A de alguma forma quebrava o módulo Z.

O negócio queria custos de infraestrutura mais baixos, mais flexibilidade de desenvolvimento, time-to-market mais rápido, e a capacidade de escalar features individuais independentemente. Clássico - mas o diabo está nos detalhes.

Microserviços: Hype vs Realidade

Sejamos honestos - microserviços resolvem problemas específicos, não todos os problemas. Aqui está o que realmente guiou nossa decisão:

  • Independência de deployment - entregar o módulo de pagamento sem tocar no auth de usuário
  • Contenção do blast radius - quando (não se) as coisas quebram, elas quebram pequeno
  • Persistência poliglota - usar Postgres para transações, Redis para sessões, MongoDB para documentos
  • Ownership do time - fronteiras claras significam responsabilidade clara
  • Scaling direcionado - escalar o serviço de busca durante picos, não o app inteiro

Deep Dive de Arquitetura

Construímos sobre Domain-Driven Design, mas não a versão acadêmica. Bounded contexts emergiram de conversas reais do time, não de exercícios no quadro branco. Nossos princípios:

  • Fronteiras de agregados definem fronteiras de serviços - se é uma transação, é um serviço
  • Eventos sobre chamadas síncronas - coreografia vence orquestração na maioria dos casos
  • Contratos de API como cidadãos de primeira classe - quebre o contrato, quebre o build
  • Arquitetura shared-nothing - cada serviço possui seus dados, ponto
  • Observabilidade não é opcional - se você não pode rastrear, não faça deploy

A Stack (E Por Quê)

Cada escolha de ferramenta foi um tradeoff. Aqui está onde chegamos:

text
Infrastructure:
├── Kubernetes (EKS) → Deployments declarativos, self-healing
├── Istio service mesh → mTLS, traffic shaping, circuit breaking
├── Kong API Gateway → Rate limiting, auth, transformação de requests
│
Messaging:
├── Kafka → Event backbone, retenção de 7 dias
├── Redis Streams → Pub/sub leve, dados efêmeros
│
Data Layer:
├── PostgreSQL → Transações ACID, JSONB para flexibilidade
├── MongoDB → Document store para audit logs, activity feeds
├── Redis Cluster → Session store, caching distribuído
├── Elasticsearch → Busca full-text, agregação de logs
│
Observability:
├── OpenTelemetry → Instrumentação vendor-agnostic
├── Prometheus + Thanos → Métricas com armazenamento de longo prazo
├── Grafana → Dashboards, alerting
├── Jaeger → Distributed tracing
│
CI/CD:
├── GitLab CI → Build, test, security scanning
├── ArgoCD → GitOps deployments
├── Sealed Secrets → Gerenciamento de secrets nativo K8s

Patterns Que Nos Salvaram

Teoria é legal. Aqui está o que realmente nos manteve fora de problemas:

**Transactional Outbox** - Em vez de dual-writes (banco de dados + message broker), escrevemos eventos em uma tabela outbox na mesma transação. Um processo separado os publica. Atômico. Confiável. Sem pesadelos de transações distribuídas.

**Event Sourcing (onde importa)** - Para fluxos de pagamento e caminhos críticos de auditoria, armazenamos eventos, não estado. Cada mutação é um evento imutável. Debugar problemas de produção reproduzindo sequências exatas. Times de compliance adoram.

**CQRS com Projeções** - Modelos de escrita otimizados para validação, modelos de leitura otimizados para queries. Eventual consistency é ok para views de leitura. O time de reporting obtém suas tabelas desnormalizadas sem poluir o write path.

**Saga Orchestration** - Processos de negócio de longa duração (onboarding, settlement de pagamentos) como máquinas de estado explícitas. Transações compensatórias em caso de falha. Sem estados parciais órfãos.

**Circuit Breaker + Bulkhead** - Hystrix está morto, mas os patterns não. Resilience4j lida com circuit breaking, rate limiting e retry com backoff. Thread pools separados para integrações externas.

O Fluxo de Pagamento: Arquitetura Real

É assim que dinheiro real se move pelo sistema:

text
┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│ API Gateway │──────│  Payment    │──────│   Fraud     │
│   (Kong)    │ gRPC │  Service    │ Event│  Detection  │
└─────────────┘      └──────┬──────┘      └──────┬──────┘
                            │                    │
                     PaymentInitiated      FraudCheckCompleted
                            │                    │
                            ▼                    ▼
                     ┌─────────────┐      ┌─────────────┐
                     │   Outbox    │      │    Risk     │
                     │   Table     │      │   Scoring   │
                     └──────┬──────┘      └──────┬──────┘
                            │                    │
                     Debezium CDC           RiskAssessed
                            │                    │
                            ▼                    ▼
                     ┌─────────────────────────────────┐
                     │         Kafka Topics            │
                     │  payments.initiated             │
                     │  fraud.checked                  │
                     │  risk.assessed                  │
                     │  payments.completed             │
                     └─────────────────────────────────┘
                                    │
            ┌───────────────────────┼───────────────────────┐
            ▼                       ▼                       ▼
     ┌─────────────┐         ┌─────────────┐         ┌─────────────┐
     │   Ledger    │         │   Order     │         │Notification │
     │   Service   │         │   Service   │         │   Service   │
     └─────────────┘         └─────────────┘         └─────────────┘

Estratégia de Testes Que Realmente Funciona

Esqueça a pirâmide de testes por um momento. Em sistemas distribuídos você precisa de:

  • Contract tests (Pact) - Serviços falam com stubs, não dependências reais. Contratos quebram em CI, não produção
  • Consumer-driven contracts - Consumers definem o que precisam, providers provam que entregam
  • Chaos testing (Chaos Monkey, Litmus) - Matar pods aleatoriamente. Injetar latência. Provar resiliência
  • Synthetic monitoring - Sondas de produção contínuas para jornadas de usuário críticas
  • Load testing como validação - Validamos que a arquitetura podia lidar com 80x o tráfego original através de rigorosos load tests antes do lançamento
  • Canary deployments - 1% do tráfego para novas versões, rollback automático em pico de erros

O Que Realmente Aconteceu

No início, focamos em decompor o monólito - identificando costuras, estrangulando o sistema antigo serviço por serviço. No começo, foi difícil. Habilidades de debugging distribuído faltavam, traces estavam incompletos, e partições de rede expunham bugs de consistência.

Depois de alguns meses, as coisas clicaram. Times possuíam seus serviços end-to-end. Deployments se tornaram não-eventos. O time de platform engineering tinha construído golden paths suficientes para que subir um novo serviço levasse horas, não semanas.

Quando o tempo passou e a arquitetura amadureceu, os resultados falaram por si:

  • Tempos de resposta caíram de 850ms p99 para menos de 120ms p99
  • Zero-downtime deployments - janelas de manutenção viraram memória
  • Custos de infraestrutura reduzidos em 35% apesar de maior tráfego
  • Frequência de deployment: de mensal para 50+ deploys diários
  • Tempo médio de recuperação: menos de 5 minutos para a maioria dos incidentes

As Lições Difíceis

Nem tudo foi suave. Aqui está o que doeu:

**Eventual consistency é uma feature, não um bug** - Mas explique isso para o PM perguntando porque o dashboard mostra dados desatualizados. Projete para isso. Comunique.

**Distributed tracing ou morte** - Sem correlation IDs e propagação adequada de trace context, debugging é arqueologia. Auto-instrumentação do OpenTelemetry é seu amigo.

**Evolução de schema é difícil** - Avro com schema registry. Apenas mudanças backwards-compatible. Breaking changes requerem um novo topic.

**Kubernetes é um sistema operacional** - Não lute contra ele. Aprenda. Resource limits, liveness probes, pod disruption budgets - existem por razões.

**Time de plataforma não é negociável** - Alguém precisa possuir as abstrações de infraestrutura. Caso contrário, cada time reinventa a roda.

Quando NÃO Fazer Microserviços

Falando sério - microserviços são caros. Considere alternativas se:

  • Seu time é pequeno - overhead de coordenação mata velocidade
  • Fronteiras de domínio não estão claras - você vai desenhá-las errado e sofrer dores de migração
  • Você não tem capacidade de platform engineering - complexidade de infraestrutura explode
  • Requisitos de latência são extremos - hops de rede se somam
  • Seu monólito só precisa de melhor modularização - tente primeiro um monólito modular

Conclusões

No final dessa jornada, tínhamos uma plataforma que deployava continuamente, escalava sob demanda, e dava aos times ownership real. O negócio conseguiu o que pediu: custos mais baixos, delivery mais rápido, e flexibilidade para evoluir.

Valeu a pena? Para essa escala e esses requisitos, absolutamente. Mas começamos com um monólito modular e só extraímos serviços quando a dor era real.

Pensando nesse tipo de transformação? Comece com o problema, não a solução.

Key Takeaways

Arquitetura de microserviços não é sobre seguir tendências - é sobre resolver desafios específicos de scaling e organização. Os patterns que cobrimos (transactional outbox, event sourcing, CQRS, saga orchestration) não são exercícios teóricos; são soluções testadas em batalha para problemas reais de sistemas distribuídos.

Validamos que a arquitetura podia sustentar 80x o tráfego original através de load testing abrangente. Deployments passaram de eventos mensais para não-eventos acontecendo dezenas de vezes por dia. Esse é o retorno quando você faz os fundamentos direito.

Tem desafios de arquitetura com os quais está lutando? Vamos falar sobre patterns.

#microservices #kubernetes #docker #scalability #devops #cloud-native #event-sourcing #cqrs #ddd
E

Engineering Team

Senior Solutions Architects

Construímos sistemas distribuídos desde antes de 'microserviços' ser um termo. Nossas cicatrizes contam histórias.