Arquitetura: Monolito Modular, Microserviços e o Caminho do Meio
Quando o Monolito é a escolha certa, Lei de Conway explicada, os custos reais de microserviços (distributed systems fallacies), Monolito Modular como ponto de partida, e os sinais de que chegou a hora de extrair um serviço.
"Todo mundo quer microserviços" é uma frase que engenheiros experientes ouvem de desenvolvedores juniores animados — e que costuma anteceder meses de dor com sistemas distribuídos desnecessariamente complexos. A indústria romanticizou os microserviços porque é isso que Google, Netflix e Amazon usam. O que ninguém menciona é que essas empresas construíram monolitos por anos antes de migrar, têm times dedicados de plataforma para abstrair a complexidade de serviços distribuídos, e ainda enfrentam problemas de consistência eventual que um monolito simplesmente não tem.
A questão não é 'monolito ou microserviços' — é 'qual arquitetura resolve os problemas que eu tenho hoje, sem criar problemas que eu não tenho'. Este artigo apresenta as três arquiteturas do espectro e o framework para decidir onde você está.
Monolito: As Vantagens que Ninguém Menciona
- Simplicidade operacional — um único processo, um único deploy, um único banco de dados. Rollback é um
git revert+ redeploy. - Chamadas locais — chamar o módulo de pagamento dentro do mesmo processo é uma chamada de função de nanosegundos. Em microserviços, é uma chamada HTTP com potencial de falha de rede, timeout e latência.
- Transações ACID simples —
BEGIN; UPDATE accounts; UPDATE inventory; COMMIT;— tudo ou nada. Em microserviços, você precisa de Sagas ou Two-Phase Commit para consistência distribuída. - Debug simples — stack trace de um único processo. Em microserviços, você precisa de distributed tracing (Jaeger, Zipkin) para rastrear uma requisição entre 5 serviços.
- Time pequeno funciona melhor — 5 devs gerenciando 20 microserviços = 4 microserviços por dev. Muito overhead de infraestrutura para pouco produto entregue.
O Monolito Modular: O Ponto de Partida Correto
O Monolito Modular é o meio-termo inteligente: código em um único repositório e processo, mas organizado por contextos de negócio (Bounded Contexts do DDD), com fronteiras claras entre módulos. Quando um módulo precisar escalar independentemente, a extração para microserviço é cirúrgica:
src/
├── modules/
│ ├── users/ # Contexto de usuários
│ │ ├── domain/ # Entidades, interfaces, regras de negócio
│ │ ├── application/ # Use Cases
│ │ ├── infra/ # Repositórios TypeORM, controllers, rotas
│ │ └── index.ts # Façade pública do módulo
│ │
│ ├── payments/ # Contexto de pagamentos
│ │ ├── domain/
│ │ ├── application/
│ │ ├── infra/
│ │ └── index.ts
│ │
│ └── notifications/ # Contexto de notificações
│ └── ...
│
├── shared/ # Código compartilhado entre módulos
│ ├── errors/
│ ├── providers/
│ └── middlewares/
│
└── server.ts
# Regra de fronteira: módulos se comunicam apenas através das façades
# PROIBIDO: import direto de outro módulo (payments importa UserModel de users)
# PERMITIDO: users/index.ts exporta apenas DTOs e interfaces públicasA Lei de Conway e os Microserviços
"Organizações que projetam sistemas tendem a produzir designs que são cópias da estrutura de comunicação dessas organizações." — Melvin Conway. Microserviços fazem sentido quando você tem times independentes — cada time é responsável por um serviço e pode deployar sem coordenar com os outros. Com um time único, microserviços adicionam overhead sem o benefício da autonomia.
Os Custos Reais dos Microserviços
As 8 Falácias dos Sistemas Distribuídos (Peter Deutsch, Sun Microsystems) listam as premissas falsas que engenheiros assumem:
- A rede é confiável
- A latência é zero
- A largura de banda é infinita
- A rede é segura
- A topologia não muda
- Existe apenas um administrador
- O custo de transporte é zero
- A rede é homogênea
Cada chamada entre microserviços pode falhar, ter latência variável, ter timeout, ter retry inconsistente. Você precisa implementar: Circuit Breakers (parar de chamar um serviço que está falhando), Retry com backoff exponencial, Timeout por chamada, Health checks de dependências, Distributed tracing e Consistência eventual em vez de transações ACID.
Sinais de que É Hora de Extrair um Microserviço
- Gargalo de hardware específico — o módulo de processamento de vídeo usa 90% da CPU, mas você não quer escalar toda a API horizontalmente só por causa dele.
- Times independentes — você tem um time de 10 pessoas só para o módulo de pagamentos. Coordenar cada deploy com o time de usuários está custando horas.
- Requisitos de compliance diferentes — o módulo de PCI DSS (cartões) tem requisitos de segurança e auditoria que conflitam com o resto do sistema.
- Stack tecnológica diferente — o módulo de ML/AI roda melhor em Python. Extraí-lo como microserviço permite usar a linguagem certa para o problema certo.
- Estabilidade independente — um bug no módulo de relatórios derrubar a API de login é inaceitável, mesmo que sejam módulos separados no mesmo processo.
A estratégia de Sam Newman (Building Microservices): comece com um monolito modular bem estruturado. Encontre os pontos de corte naturais (bounded contexts). Extraia para microserviço quando você tiver um problema real que o monolito não consegue resolver — não antes. Sistemas que começam como microserviços raramente chegam à produção no prazo; sistemas que começam como monolito modular extraem serviços na hora certa.
Conclusão
Microserviços não são um upgrade de monolitos — são uma troca de complexidades. Você troca a complexidade de deploy/escalonamento de um único serviço pela complexidade de sistemas distribuídos (falhas de rede, consistência eventual, distributed tracing). Para a maioria dos projetos em fase de crescimento, um Monolito Modular com fronteiras claras de contexto entrega 80% dos benefícios com 20% dos custos. Quando o crescimento real tornar o monolito um problema, a extração de serviços específicos será natural e fundamentada — não uma aposta arquitetural antecipada.