04 - Microsserviços (Microservices)
video: https://youtu.be/... no frontmatter da nota.Microsserviços (Microservices)
Microsserviços são um estilo arquitetural em que o sistema é decomposto em serviços pequenos, cada um responsável por uma capacidade de negócio, implantável de forma independente e dono exclusivo de seus dados. Os serviços se comunicam por contratos de rede, geralmente API síncrona e mensageria assíncrona, e nenhum deles acessa o banco do outro. O estilo ganhou força com a escala de organizações como Amazon e Netflix, e a motivação central nunca foi técnica isoladamente: é permitir que muitos times avancem em paralelo com atrito mínimo. Em produção, o preço dessa autonomia é assumir, dentro da plataforma, todos os problemas distribuídos que o monolito não tinha: falha parcial de rede, consistência eventual e observabilidade de fluxos que atravessam dezenas de processos.
Conceito e Propriedades Essenciais
O que define um microsserviço não é o tamanho em linhas, e sim a combinação de autonomia de implantação, propriedade de dados e alinhamento com o negócio. Um serviço é uma unidade de deploy que pode ir a produção sozinha, sem coordenar release com vizinhos, e uma unidade de dado que ninguém mais consulta por baixo. Sam Newman resume como serviço pequeno focado em fazer uma coisa bem feita, e Martin Fowler enfatiza que a propriedade essencial é a independência de deploy; sem ela, o sistema é um monolito distribuído com custo extra.
O histórico organizacional ajuda a entender o desenho: a Amazon chegou à decomposição por necessidade de coordenação entre centenas de times, com a regra de duas pizzas como tamanho de equipe, e a Netflix consolidou o modelo com plataformas de entrega e resiliência dedicadas. O estilo, portanto, nasceu para escalar organizações, e escalar a arquitetura foi consequência. Copiar o desenho sem a necessidade organizacional correspondente costuma produzir o custo sem o benefício.
Propriedades que separam microsserviço de serviço pequeno qualquer:
- Capacidade de negócio como fronteira: o serviço existe para uma pergunta do domínio, como cobrança ou catálogo, e não para uma camada técnica como "serviço de DAO".
- Deploy independente: qualquer serviço sobe a produção sem lockstep, o que exige contratos compatíveis retroativamente.
- Banco por serviço: o dado tem um único dono; outros serviços consomem por API ou por eventos, nunca por JOIN no esquema alheio.
- Falha como hipótese de projeto: timeout, retry, circuit breaker e degradação elegante fazem parte do contrato, não são improvisos.
- Equipe como unidade de operação: o time que constrói o serviço o opera, fechando o ciclo de feedback de design e produção.
Banco por Serviço e Consistência Eventual
Dado distribuído é a consequência mais dura do estilo. Sem banco compartilhado, não existe transação ACID cruzando serviços, e operações que eram um commit viram fluxos distribuídos que precisam convergir com o tempo. O padrão saga coordena esses fluxos como sequência de transações locais, com compensações para desfazer o caminho em caso de falha, e o padrão outbox garante que a publicação do evento acompanhe atomicamente a escrita local, eliminando a janela clássica de evento perdido.
# Saga coreografada: pedido criado dispara reações em cadeia
evento:
nome: PedidoCriado # fato no passado; não é comando
versao: 2 # contrato versionado entre serviços
produtor: servico-pedidos # escrito via outbox na mesma transação do INSERT
payload:
pedido_id: "9f3c2a71"
cliente_id: "c81e5b04"
valor_total: 249.90
consumidores:
servico-estoque:
acao: reservar_itens # transação local própria
idempotencia: chave por pedido_id; reentrega repete sem duplicar efeito
em_falha: publica ReservaRecusada
servico-pagamentos:
acao: autorizar_pagamento
em_falha: publica PagamentoRecusado
compensacao: estoque escuta e libera a reserva
servico-pedidos:
escuta: ReservaAceita e PagamentoAprovado
atualiza_status: de CRIADO para CONFIRMADO, com consistência eventual
O desenho acima troca uma transação distribuída por três locais e eventos de compensação. A contrapartida aparece na interface com o usuário: o pedido pode existir um instante sem reserva confirmada, e o produto precisa comunicar confirmação em andamento em vez de fingir atomicidade que não existe. Sistemas que não aceitam essa janela, como movimento bancário em tempo real entre contas do mesmo agregado, devem manter essa operação dentro de um único serviço e banco, e não espalhá-la pela malha.
Comunicação: Síncrona versus Assíncrona
A escolha do meio de comunicação decide o perfil de falha do fluxo. Chamada síncrona é direta e imediata, mas acopla disponibilidade: se o destino está fora, quem chama fica bloqueado ou falha junto. Mensageria desacopla em tempo, absorve picos e permite que o consumidor esteja em manutenção enquanto o produtor continua; em troca, traz entrega duplicada, atraso variável e a obrigação de idempotência. Fluxos críticos de leitura tendem a síncrono com timeout e circuit breaker; reações de negócio e integrações tendem a eventos com saga.
| Meio | Acoplamento | Perfil de falha | Uso típico |
|---|---|---|---|
| REST ou gRPC síncrono | Alto: destino precisa estar de pé | Falha imediata propagada | Consulta em tempo real de suporte à decisão |
| Fila com um consumidor | Baixo em tempo | Retardo aceitável, sem perda | Distribuição de trabalho e absorção de pico |
| Pub-sub de eventos | Baixo entre produtores e consumidores | Reações independentes | Notificar múltiplos interessados de um fato |
| Chamada em cadeia | Acumulativo ao longo da cadeia | Latência soma; p99 cresce | Evitar; prefira agregação ou eventos |
Decomposição: de Onde Vêm os Limites
Fronteiras mal cortadas são a causa raiz da maioria das experiências ruins com o estilo. Os cortes mais duráveis saem de contextos delimitados do DDD, de estruturas de equipe e de padrões de mudança observados no código, e não de tabelas ou camadas técnicas. Um serviço que muda junto com outro a cada sprint está cortado no lugar errado, e o acoplamento apenas trocou de meio, de chamada local para chamada de rede.
- Contexto delimitado: cada serviço cobre uma linguagem ubíqua coerente; se os termos conflitam, são contextos diferentes.
- Dono e ciclo de mudança: serviços cortados de modo que um time típico consiga entendê-los e operá-los por inteiro.
- Perfil de escala e tecnologia: quando um pedaço exige runtime ou capacidade muito distintas, a fronteira ganha justificativa extra.
- Caminho de rollback: cada corte deve poder voltar atrás com custo razoável, reforçando a preferência por fronteiras naturais.
Deploy Independente e Contratos
Deploy independente só existe sobre contratos compatíveis retroativamente: um serviço novo pode adicionar campos, mas não remover ou mudar semântica de campos que consumidores usam. Testes de contrato dirigidos pelo consumidor verificam em CI que a mudança proposta não quebra quem consome hoje, e versionamento explícito com janela de depreciação organizada substitui o acordo tácito. Na borda externa, API gateway e backend for frontend concentram autenticação, agregação e adaptação para cada cliente, evitando que o navegador fale com dez serviços diretamente.
A rotina de implantação também muda de patamar: canary e blue-green por serviço, feature flags para separar deploy de lançamento e rollback autônomo por serviço são práticas padrão. Sem esse aparato, o lockstep volta disfarçado, com releases coordenados em planilha, e o sistema cobra o custo da distribuição sem entregar o benefício.
Complexidade Operacional
O estilo transfere complexidade do design do aplicativo para a operação da plataforma, e é honesto reconhecer o valor dessa conta antes de adotá-lo. Falhas parciais são o estado normal: um serviço de terceiro lento pode virar fila de threads esgotadas no gateway se não houver timeout e circuit breaker. Depurar uma transação que atravessa oito serviços exige tracing distribuído com correlação ponta a ponta. Versionar, monitorar, alertar e prestar conta de dezenas de serviços exige plataforma dedicada, e times sem esse lastro descobrem que a produtividade caiu, não subiu.
Investimentos que se tornam pré-requisitos, não luxos:
- Observabilidade distribuída: traces correlacionados, métricas por serviço e logs com identificador comum de fluxo.
- Resiliência no caminho de rede: timeouts explícitos, retentativas com backoff, circuit breakers e limites de concorrência.
- Plataforma de entrega: pipeline por serviço, infraestrutura como código e ambientes que se criam sem pedido humano.
- Governança de contratos: registro de APIs e eventos, checagem automática de compatibilidade e política de depreciação.
- Autonomia com guard rails: templates de serviço, padrões de segurança e custo visível por equipe, sem burocracia central.
Anti-padrões Comuns
| Anti-padrão | Sintoma típico | Correção usual |
|---|---|---|
| Nanoserviço | Operação simples salta por cinco serviços | Agregar em serviço por capacidade de negócio |
| Monolito distribuído | Serviços só funcionam juntos; deploy em lockstep | Banco por serviço e contratos versionados |
| Banco compartilhado | JOIN entre esquemas de serviços distintos | Definir dono do dado e expor API ou evento |
| Serviço-camada técnica | "servico-validador" sem sentido de negócio | Recortar por contexto delimitado, não por tipo |
| Saga sem idempotência | Dados duplicados a cada reentrega | Chave de idempotência e deduplicação no consumidor |
| Coreografia sem rastro | Ninguém sabe onde o fluxo parou | Tracing por evento e fluxos críticos orquestrados |
O nanoserviço merece ênfase: fragmentar além da capacidade de negócio multiplica custo de rede, deploy e observabilidade sem comprar autonomia nenhuma, porque as partes nunca sobem sozinhas. A régua prática é a pergunta de deploy: se dois serviços precisam subir juntos para nada quebrar, eles são um serviço mal cortado em dois.
Quando Não Usar
A resposta mais frequente de quem acompanha casos reais é monolith first: comece monolito modular, e extraia serviços quando gatilhos reais aparecerem. Adotar microsserviços sem maturidade operacional é comprar complexidade sem receber retorno, e o custo cobra juros justamente no momento em que o produto precisa evoluir rápido.
Condições que recomendam adiar a decomposição:
- Domínio ainda em descoberta: fronteiras instáveis geram re-divisões caras de serviço e de dado a cada sprint.
- Equipe pequena: um ou dois times raramente colhem mais atrito de coordenação do que o alívio que a distribuição traria.
- Plataforma ausente: sem CI/CD maduro, observabilidade e gestão de ambientes, cada serviço vira uma dor órfã.
- Latência crítica de fluxo interno: cadeias de chamadas somam milissegundos; para tráfego extremamente sensível, o processo único ganha.
Existe também o caminho híbrido, que é o mais comum em empresas reais: um monolito modular para o núcleo transacional, com alguns serviços extraídos onde há gatilho concreto, como processamento de mídia, recomendação ou integração de parceiro. Essa configuração entrega autonomia onde ela paga, mantém transações locais onde elas importam e cria terreno de aprendizado para uma decomposição futura, se ela vier a ser necessária.
Erros Comuns e Boas Práticas
| Sintoma em produção | Causa provável | Mitigação |
|---|---|---|
| Latência p99 crescente sem pico de carga | Cadeia síncrona longa entre serviços | Redesenhar fluxo com mensageria e agregação |
| Incidente sem causa localizada | Falta de correlação ponta a ponta | Tracing distribuído obrigatório nos templates |
| Dado inconsistente entre serviços | Saga sem compensação ou sem idempotência | Revisar fluxo com outbox, idempotência e monitor |
| Deploy de X quebra Y em produção | Contrato sem verificação de compatibilidade | Testes de contrato dirigidos pelo consumidor em CI |
| Custo de nuvem dobrando em silêncio | Serviços espalhados sem visibilidade de custo | Tagging por serviço e relatório de custo por time |
Boas práticas que sustentam o estilo em produção:
- Corte por contexto delimitado: fronteira de negócio sobrevive; fronteira técnica cria acoplamento disfarçado.
- Assuma consistência eventual como produto: desenhe a experiência do usuário para confirmações assíncronas, sem fingir atomicidade.
- Trate cada serviço como produto: dono nominal, SLO publicado e suporte dentro do horário do time que o construiu.
- Automatize o padrão desde o primeiro serviço: template com pipeline, observabilidade e segurança prontos define o piso de qualidade.
- Reavalie fronteiras com dados de produção: padrões de mudança conjunta e de tráfego revelam cortes que merecem ser unidos ou separados.
- Defina SLO por serviço e por fluxo: a saúde precisa ser medida no efeito final do negócio, não apenas na média de cada peça.