Quase todo mundo confunde microserviços com SOA e acaba pagando caro por isso
Eu já vi time inteiro migrar para microserviços achando que estava resolvendo o problema da arquitetura orientada a serviços tradicional. O resultado foi exatamente o oposto: mais acoplamento disfarçado, deploy mais lento e operação insuportável. Vou explicar o que é diferente da arquitetura orientada a serviços, sem rodeio.
O que realmente é diferente da arquitetura orientada a serviços
SOA nasceu nos anos 2000 como uma forma de quebrar monolitos em serviços menores. O problema é que a implementação prática de SOA quase sempre caiu na armadilha do ESB (Enterprise Service Bus) centralizado. Todo serviço conversava com o ônibus, o ônibus era um gargalo único, e quando ele falhava, tudo parava. A complexidade não desapareceu, só mudou de lugar. O que eu vejo hoje sendo chamado de "arquitetura de microserviços" é uma resposta direta a isso. A diferença fundamental não é técnica no sentido de linguagem ou framework. É uma mudança de postura em relação à propriedade e ao acoplamento. Em SOA, os serviços são compartilhados, governados por padrão centralizado e geralmente construídos pensando em reutilização ampla. Em microserviços, cada time dono de um serviço decide tudo: tecnologia, deploy, banco de dados, contratos. E a reutilização não é o objetivo principal.
Isso parece bom no papel. Na prática, eu já vi o seguinte: time A migrou um módulo inteiro para um serviço isolado com 3 desenvolvedores. O banco de dados permaneceu compartilhado porque ninguém teve coragem de refatorar a migração. Depois de seis meses, o serviço tinha 47 endpoints porque cada nova feature precisava de uma rota, e não havia domínio claro. O resultado foi um microserviço com problema de monolito distribuído. O tempo de resposta piorou 40% porque cada chamada interna virou uma requisição HTTP atravessando a rede.
O que funciona de verdade na prática
A coisa mais importante que eu aprendi é que o domínio define o limite dos serviços, não a funcionalidade técnica. Se você agrupar por camadas — um serviço para autenticação, outro para relatórios, outro para pedidos — você vai repetir a arquitetura em camadas do monolito em versão distribuída. O problema se mantém, só fica mais difícil de debugar. O modelo que eu recomendo, depois de ver vários fracassos, é basear os serviços em bounded contexts do DDD. Cada serviço corresponde a uma responsabilidade de negócio autônoma. O serviço de "pagamento" não sabe se o usuário está ativo ou não. Ele recebe um token e processa. Se precisar de informação do usuário, chama outro serviço. Isso elimina o acoplamento direto, mas introduz outro problema que pouca gente menciona: consistência eventual.
Aqui vai um exemplo bem específico que eu enfrentei. Tínhamos um serviço de cadastro que emitia um evento de "usuário criado". Outro serviço de faturamento escutava esse evento e criava a conta. Quando o serviço de faturamento caía durante uma manutenção não programada, os eventos se acumulavam na fila. Quando ele voltava, processou 3.000 eventos de uma vez, estourou o timeout de conexões com o banco e derrubou também o serviço de notificações que compartilhava o mesmo cluster. Eu resolvi isso implementando um delay exponencial com retry limitado a 5 tentativas, e coloquei um circuit breaker na chamada do serviço de faturamento. O custo foi aceitar que a conta do usuário ficava visível por até 4 minutos após o cadastro. Para o negócio, isso foi aceitável. Para o time de suporte, foi um pesadelo de tickets.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Contratos e evolução deles
Um dos pontos que mais gera dor em arquiteturas diferentes da arquitetura orientada a serviços é a gestão de contratos entre serviços. Em SOA, o ESB frequentemente intermediava as mudanças de formato. Em microsserviços, cada serviço expõe seu próprio contrato, normalmente via REST com JSON ou gRPC. Se você mudar o esquema de um payload e um consumidor ainda está na versão anterior, o sistema quebra silenciosamente. Não há um ponto central onde você pode colocar um mapeamento de transformação. A solução que eu uso hoje é versionar todos os contratos explicitamente. A URL já carrega a versão, ou o header de conteúdo negocia. Mas o mais importante é manter compatibilidade para trás. Nunca remova um campo de um contrato existente. Adicione novos campos com valor default. Se precisa de uma mudança breaking, lance uma nova versão e deixe os consumidores migrarem no tempo deles. Isso parece simples, mas eu vi time tentar "otimizar" contratos removendo campos que achavam que ninguém usava. Três serviços downstream quebraram em produção num terça-feira à noite.
Observabilidade não é opcional
Em um monolito, quando algo dá errado, você vê o stack trace. Em uma arquitetura de serviços distribuídos, você tem que reconstruir a jornada de uma requisição que passa por 8 serviços diferentes, cada um com logs em formatos distintos, hosts diferentes e timestamps que às vezes não estão sincronizados. Eu já perdi duas noites tentando rastrear um problema que, em última análise, era um campo numérico truncado num serviço que eu nem sabia que existia naquela trilha. O mínimo que você precisa ter instalado antes de colocar qualquer coisa em produção é tracing distribuído, logs centralizados com correlação por request ID, e métricas de latência e taxa de erro por serviço. Jaeger ou otel para tracing, ELK ou Loki para logs, Prometheus com Grafana para métricas. Sem isso, você está voando cego. E eu recomendo começar a implementar isso antes do primeiro deploy, não depois que o problema aparecer.
Quando NÃO fazer isso
Eu tenho que ser honesto aqui. Arquitetura de microsserviços não é para tudo. Se você tem uma equipe de até 5 desenvolvedores, produto em fase inicial, e o tráfego é baixo, microsserviços vão apenas adicionar complexidade operacional sem benefício real. O overhead de deploy, monitoramento, testing de integração entre serviços, e a dificuldade de fazer alterações cross-service compensam apenas quando o time cresce a ponto de o monolito se tornar impossível de sem risco. A regra prática que eu uso é: se mais de 3 times precisam coordenar deployments no mesmo sistema, aí sim faz sentido considerar a separação. Também não recomendo para sistemas que dependem fortemente de transações ACID entre múltiplas áreas de negócio. A compensação via Saga resolve parcialmente, mas adiciona uma camada de complexidade que muitas vezes não vale a pena. Nesse caso, um monolito bem estruturado com módulos bem separados dentro dele é mais produtivo e mais fácil de manter.
A questão do deploy
Uma vantagem real dos microsserviços é o deploy independente. Você consegue atualizar um serviço sem tocar nos outros. Mas isso pressupõe que os serviços sejam realmente independentes na prática, não só no nome. Eu vi caso onde o serviço de catálogo e o de preço eram deploys separados, mas o frontend chamava os dois em sequência na mesma tela. Qualquer mudança no contrato do serviço de preço obrigava atualização imediata do frontend. A independência era ilusória. O que ajuda aqui é ter interfaces estáveis e um esforço real de desacoplamento. API contracts com versionamento, eventos assíncronos quando possível, e um esforço consciente de minimizar chamadas síncronas entre serviços. Quanto menos você precisa chamar outro serviço para fazer uma operação simples, melhor. Se uma operação precisa de 6 chamadas síncronas em cascata, algo está errado no design do domínio.
Infraestrutura como parte do design
Não dá pra ignorar a infraestrutura. Microsserviços exigem containerização, orquestração, service discovery, e pipelines de CI/CD robustos. Eu já vi startup tentar rodar 20 microsserviços em VMs EC2 sem Kubernetes. O custo de infraestruturatriplicou, o tempo de deploy passou de 5 minutos para 45 minutos, e a equipe de DevOps ficou sobrecarregada mantendo os scripts de provisionamento. Isso é um problema que muitos subestimam no início. Kubernetes se tornou o padrão do setor, mas ele traz sua própria complexidade. Se você não tem experiência com containers, comece com um platform mais simples como Render, Fly.io, ou até mesmo ECS da AWS antes de pular para K8s. O tempo gasto aprendendo K8s pode ser melhor investido entendendo o domínio do negócio que você está servindo.
Resumo sem resumo
O que é diferente da arquitetura orientada a serviços é, na prática, uma mudança de mentalidade sobre propriedade, autonomia e limites de domínio. SOA tentou resolver a fragmentação com centralização. Microsserviços resolvem a centralização com fragmentação, e aí entra a parte difícil: gerenciar essa fragmentação sem perder a capacidade de entregar valor. Funciona bem quando o time é grande, o domínio é complexo, e a infraestrutura está preparada. Não funciona bem quando você está usando a complexidade como justificativa para não amadurecer a arquitetura interna. A maioria dos problemas que eu vejo não é técnica. É organizacional.