Voltar para o Blog

Microserviços vs Monolito: Quando Cada Um Faz Sentido na Prática

PT 🇧🇷Artigo12 min de leitura
#microservicos#arquitetura#design-de-software#sistemas-distribuidos#pragmatismo

Microserviços vs Monolito: Quando Cada Um Faz Sentido na Prática

Vamos começar com o que ninguém diz em voz alta: a maioria dos times que adotou microserviços não o fez porque seus problemas de engenharia exigiam isso. Fizeram porque microserviços estavam na moda, e "somos uma empresa de microserviços" soava mais sério do que "temos um monolito em Rails."

O resultado, em muitos casos, é um sistema distribuído com toda a complexidade dos microserviços e nenhum dos benefícios — porque os benefícios dos microserviços só aparecem em uma intersecção particular de escala, estrutura de time e maturidade operacional que a maioria das organizações nunca alcança.

Este artigo não é um argumento a favor de monolitos em vez de microserviços, nem o contrário. É uma tentativa de dar-lhe os trade-offs reais — custo operacional, latência, observabilidade, complexidade organizacional — para que você possa tomar a decisão corretamente para o seu contexto, não o contexto de outra pessoa.

O que um monolito realmente é

A palavra "monolito" foi usada como pejorativo por tanto tempo que vale a pena pausar para defini-la corretamente.

Um monolito é uma única unidade deployável contendo toda a funcionalidade da aplicação. Pode ser um único processo (uma aplicação Rails, Django ou Spring Boot) ou múltiplos processos que são deployados juntos (o "monolito modular" ou "monolito majestoso"). A característica definidora é que o deploy é atômico — você lança tudo junto.

Monolitos não são inerentemente bagunçados ou mal estruturados. Um monolito bem projetado tem fronteiras de módulo internas claras, aplicadas por convenções de código, regras de visibilidade de pacotes ou funções de fitness arquitetural. A modularidade é interna em vez de no nível de serviço.

O modo de falha de um monolito é a Grande Bola de Lama — uma base de código onde tudo sabe sobre tudo, fronteiras são implícitas ou inexistentes, e toda mudança requer entender o sistema inteiro. Mas isso é uma falha de disciplina de engenharia, não uma propriedade inerente da arquitetura monolítica.

O que microserviços realmente são

Uma arquitetura de microserviços decompõe a aplicação em serviços independentemente deployáveis, cada um possuindo um contexto delimitado do domínio de negócio, comunicando-se com outros serviços pela rede (HTTP, gRPC, messaging).

As propriedades definidoras são: deployabilidade independente (você pode lançar o Serviço A sem lançar o Serviço B) e propriedade delimitada (cada serviço é possuído por um time e tem seu próprio data store).

Essas propriedades são valiosas — mas vêm a um custo frequentemente dramaticamente subestimado.

Os custos operacionais reais dos microserviços

Quando você divide um processo em serviços, troca chamadas de função in-process por chamadas de rede. Este é o trade-off central, e todo outro custo decorre dele.

Latência se compõe além das fronteiras de serviço

Em um monolito, uma chamada de função que cruza fronteiras de módulo custa nanossegundos. Em um sistema de microserviços, a chamada de rede equivalente custa milissegundos — uma diferença de cinco a seis ordens de magnitude. Para uma requisição que cruza dez fronteiras de serviço, essa latência se compõe. Some jitter de rede, lógica de retry e tratamento de timeout, e você tem um sistema distribuído onde a latência de cauda do todo é pior do que a de qualquer parte individual.

Isso não é hipotético. A Netflix publicou extensivamente sobre os desafios de gerenciar a latência P99 em um sistema onde uma única requisição de usuário pode se expandir para dezenas de microserviços. O investimento deles no Hystrix (circuit breakers), Ribbon (load balancing) e Eureka (descoberta de serviços) é uma resposta direta a esse problema — e representa um enorme investimento contínuo de engenharia.

O imposto de sistemas distribuídos é real e substancial

Todo sistema de microserviços deve resolver um conjunto de problemas que simplesmente não existem em um monolito:

Nenhum desses problemas é insolúvel. Mas cada um requer investimento: ferramental, expertise operacional e manutenção contínua. Um time de cinco precisa decidir se resolver esses problemas é um uso melhor do seu tempo do que entregar features.

Observabilidade se torna uma preocupação de primeira ordem

Depurar um bug em um monolito é relativamente simples: você tem um call stack, um debugger, reprodução local. O modo de falha é visível.

Depurar em um sistema de microserviços é fundamentalmente diferente. Uma requisição de usuário lenta pode ser causada por:

Sem distributed tracing (correlacionando logs e spans entre fronteiras de serviço usando um trace ID compartilhado), encontrar a causa raiz é um exercício de arqueologia de logs correlacionados. Isso é solucionável — mas requer investimento em infraestrutura de observabilidade que um monolito simplesmente não precisa.

Complexidade de deploy se multiplica

Um monolito tem um pipeline de deploy. Microserviços têm N pipelines de deploy, cada um com sua própria configuração CI/CD, imagem de container, definição de infraestrutura e processo de release. O overhead de coordenação cresce com N.

Pior: os deploys se tornam interdependentes mesmo em um sistema "independentemente deployável". Se o Serviço A publica um novo schema de evento que o Serviço B espera consumir, ambos devem ser deployados em uma janela coordenada ou um vai falhar. Gerenciar esses deploys coordenados em escala (100+ serviços) requer investimento significativo em ferramental.

Onde microserviços criam valor genuíno

Depois de tudo isso, microserviços têm vantagens genuínas — mas elas aparecem apenas sob condições específicas.

Escalonamento independente

Um monolito escala como uma unidade — se uma dimensão da sua aplicação é computacionalmente cara (digamos, processamento de imagens), você deve escalar o monolito inteiro para lidar com isso. Em um sistema de microserviços, o serviço de processamento de imagens pode ser escalado independentemente, com recursos dedicados correspondentes às suas necessidades específicas.

Isso importa quando diferentes partes do seu sistema têm perfis de recursos dramaticamente diferentes. Não importa quando toda a sua aplicação tem carga uniforme — o que é a grande maioria dos produtos na maioria dos estágios de crescimento.

Velocidade de deploy independente

Quando múltiplos times estão entregando features para a mesma base de código, deploys se tornam problemas de coordenação. O Time A quer lançar sua feature na terça. O Time B tem uma correção de bug que precisa até o fim do dia de segunda. O Time C está no meio de uma migração que deixa a base de código em um estado intermediário. O resultado é uma fila de deploy, longos períodos de release e times se bloqueando mutuamente.

Em um sistema de microserviços, cada time possui um serviço e faz deploy independentemente. O Time A pode lançar na terça sem nenhum conhecimento do que os Times B e C estão fazendo. Esta é a funcionalidade matadora dos microserviços em escala organizacional — e é quase inteiramente irrelevante para um único time.

Heterogeneidade tecnológica

Existem cargas de trabalho onde a ferramenta certa não é a linguagem principal do seu monolito. Um serviço de inferência de machine learning pode rodar Python. Um serviço de ingestão de dados de alta velocidade pode precisar de Go ou Rust. Um pipeline de processamento de eventos pode ser melhor expresso em Flink.

Microserviços permitem que essas cargas de trabalho vivam como serviços em sua linguagem natural, integrando-se via APIs de rede. Um monolito força tudo para uma linguagem e runtime. Essa vantagem é real, mas se aplica a uma fatia estreita de cargas de trabalho.

A dimensão de complexidade organizacional

A Lei de Conway é o insight mais subestimado em arquitetura de software: a estrutura do seu sistema tende a espelhar a estrutura de comunicação da sua organização.

Isso tem um corolário que raramente é declarado: você não pode adotar microserviços mais rápido do que pode distribuir a propriedade. Se você decompõe sua base de código em quinze serviços mas ainda tem um único time responsável por todos eles, você multiplicou a complexidade operacional sem ganhar nenhum dos benefícios organizacionais.

Microserviços fazem sentido quando:

Microserviços são um passivo quando:

O monolito modular: o meio-termo que ninguém fala o suficiente

O falso binário neste debate é monolito vs. microserviços. Há uma terceira opção que é melhor para a maioria das organizações na maioria dos estágios: o monolito modular.

Um monolito modular é uma única unidade deployável com fortes fronteiras de módulo internas, aplicadas através de estrutura de código, visibilidade de pacotes e testes arquiteturais. Cada módulo tem uma API pública definida que outros módulos devem usar — acesso interno direto é proibido por convenção ou ferramental.

Isso oferece:

Stack Overflow, Shopify e Basecamp rodam famosamente em monolitos bem mantidos. O fio comum: design modular disciplinado combinado com qualidade de engenharia excepcional.

Um framework prático de decisão

Aqui está o critério honesto:

Fique com um monolito se:

Migre para microserviços quando:

Use um monolito modular quando:

O caminho de migração que realmente funciona

Se você está movendo de um monolito para serviços porque a necessidade é real, a estratégia que consistentemente funciona:

  1. Identifique o candidato à extração pelo sinal certo — não "este módulo é grande", mas "este módulo precisa fazer deploy em uma frequência diferente do restante" ou "esta carga de trabalho precisa de recursos fundamentalmente diferentes."
  2. Aplique fronteiras de módulo primeiro, no monolito — se o módulo que você quer extrair está emaranhado com o resto da base de código, extraia as fronteiras antes de extrair o serviço.
  3. Extraia um serviço por vez, valide, então continue — o padrão strangler fig. Redirecione o tráfego para o novo serviço gradualmente. Mantenha as implementações antiga e nova até estar confiante. Não tente migração em massa.
  4. Invista em infraestrutura compartilhada antes da extração — distributed tracing, service mesh, templates CI/CD. Se isso não existir antes de você extrair o primeiro serviço, você vai construir reativamente sob pressão.

Reflexão final

A melhor arquitetura de sistema é a mais simples que resolve os problemas organizacionais e técnicos que você realmente tem hoje, com um caminho claro para evoluir quando esses problemas mudam.

Para a maioria dos produtos na maioria dos estágios, isso é um monolito modular bem estruturado. Para produtos com múltiplos times entregando para o mesmo domínio, cargas de trabalho distribuídas e maturidade operacional para sistemas distribuídos, serviços valem seu custo.

O erro é escolher sua arquitetura para corresponder a uma aspiração sobre a escala que você espera alcançar, em vez da realidade dos problemas que você tem agora. Microserviços prematuros são tão custosos quanto otimização prematura — e muito mais prejudiciais à velocidade do time.

Comece mais simples do que você acha que precisa. Evolua deliberadamente quando a pressão for real.

Newsletter

Fique à frente da curva

Insights técnicos aprofundados sobre arquitetura de software, IA e engenharia. Sem enrolação. Um e-mail por semana.

Sem spam. Cancele quando quiser.

Microserviços vs Monolito: Quando Cada Um Faz Sentido na Prática | Antonio Ferreira