Monolito vs Microserviços: Quando Cada Um Faz Sentido

Eduardo CintraEduardo Cintra
8 min read

Na jornada de arquitetura de software, poucas decisões geram tanto debate quanto a escolha entre arquiteturas monolíticas e de microserviços. Como desenvolvedor .NET que já trabalhou com ambas abordagens em diversos contextos, percebi que muitas equipes tomam essa decisão baseadas em tendências ou preferências pessoais, sem considerar adequadamente o contexto específico do projeto.

Neste artigo, vamos analisar objetivamente quando cada abordagem faz sentido, com exemplos práticos e critérios claros para guiar sua decisão.

Entendendo os Conceitos Fundamentais

Antes de comparar, vamos definir claramente o que estamos discutindo:

Arquitetura Monolítica

Uma arquitetura monolítica é caracterizada por uma base de código única onde todos os componentes da aplicação estão fortemente acoplados e são implantados como uma única unidade. Em um projeto .NET típico, isso seria uma solução com múltiplos projetos, mas implantada como uma única aplicação.

Características principais:

  • Implantação única e centralizada

  • Compartilhamento de recursos (banco de dados, cache, etc.)

  • Comunicação entre componentes via chamadas de método diretas

  • Escala vertical (aumentando recursos da máquina)

  • Consistência transacional facilitada

Arquitetura de Microserviços

Uma arquitetura de microserviços divide a aplicação em serviços pequenos, independentes e com responsabilidade única, cada um executando em seu próprio processo e se comunicando através de mecanismos leves, geralmente HTTP/REST ou mensageria.

Características principais:

  • Implantação independente de cada serviço

  • Bancos de dados dedicados por serviço

  • Comunicação via rede (APIs, mensagens)

  • Escala horizontal (aumentando o número de instâncias)

  • Consistência eventual entre serviços

Quando Monolitos Fazem Sentido

Contrariando a tendência atual que parece favorecer microserviços em qualquer cenário, existem muitas situações onde monolitos são a escolha mais sensata:

1. Startups e MVPs

Para startups e produtos em fase inicial, a velocidade de desenvolvimento e a capacidade de pivotar rapidamente são cruciais. Monolitos oferecem:

  • Menor complexidade inicial: Sem preocupações com comunicação entre serviços, latência de rede ou consistência distribuída.

  • Setup mais simples: Um único repositório, pipeline de CI/CD e ambiente de implantação.

  • Refatoração mais fácil: Mudanças que atravessam múltiplos componentes podem ser feitas em uma única base de código.

Exemplo prático: A Shopify, plataforma de e-commerce que processa bilhões em vendas anualmente, manteve uma arquitetura monolítica por muitos anos, mesmo com crescimento acelerado. Isso permitiu que eles se concentrassem em entregar valor ao cliente em vez de lidar com complexidades distribuídas prematuramente.

2. Equipes Pequenas

Equipes com poucos desenvolvedores podem se beneficiar de monolitos porque:

  • Menor sobrecarga operacional: Menos sistemas para monitorar, fazer backup e manter.

  • Conhecimento compartilhado: Todos os desenvolvedores podem entender e trabalhar em qualquer parte do sistema.

  • Menos especialização necessária: Não é preciso ter especialistas em diferentes tecnologias ou padrões de comunicação distribuída.

3. Domínios Altamente Integrados

Quando o domínio de negócio tem muitas regras que atravessam diferentes funcionalidades, um monolito pode ser mais adequado:

  • Transações ACID: Operações que precisam modificar múltiplas entidades de negócio podem usar transações de banco de dados.

  • Consistência imediata: Não é necessário lidar com estados inconsistentes temporários entre serviços.

  • Queries complexas: Consultas que agregam dados de múltiplas entidades são mais simples em um único banco de dados.

Exemplo de código: Considere uma operação de transferência bancária que precisa debitar uma conta e creditar outra, garantindo que ambas operações sejam atômicas:

// Em um monolito, isso é simples com uma transação
using (var transaction = _context.Database.BeginTransaction())
{
    try
    {
        var sourceAccount = _context.Accounts.Find(sourceAccountId);
        var destinationAccount = _context.Accounts.Find(destinationAccountId);

        sourceAccount.Balance -= amount;
        destinationAccount.Balance += amount;

        _context.SaveChanges();
        transaction.Commit();
    }
    catch
    {
        transaction.Rollback();
        throw;
    }
}

Em microserviços, isso exigiria padrões mais complexos como Saga ou 2PC (Two-Phase Commit).

Quando Microserviços Fazem Sentido

Microserviços não são uma bala de prata, mas existem cenários onde suas vantagens superam as desvantagens:

1. Equipes Grandes ou Distribuídas

Para organizações com múltiplas equipes trabalhando no mesmo produto:

  • Autonomia de equipe: Cada equipe pode desenvolver, testar e implantar seus serviços independentemente.

  • Limites claros de propriedade: Cada serviço tem proprietários definidos, reduzindo conflitos de merge e dependências entre equipes.

  • Escala organizacional: Permite que mais desenvolvedores trabalhem no mesmo sistema sem interferência mútua.

Exemplo real: A Amazon foi uma das pioneiras em microserviços, adotando a abordagem para permitir que centenas de equipes pudessem trabalhar simultaneamente em diferentes aspectos de seu e-commerce sem bloqueios mútuos.

2. Requisitos de Escala Heterogêneos

Quando diferentes partes do sistema têm necessidades de escala drasticamente diferentes:

  • Escala independente: Serviços com alta demanda podem ser escalados sem afetar o resto do sistema.

  • Otimização de recursos: Cada serviço pode ser dimensionado de acordo com suas necessidades específicas.

  • Tecnologias especializadas: Serviços com requisitos únicos podem usar tecnologias otimizadas para seu caso de uso.

Exemplo prático: Em um sistema de e-commerce, o serviço de catálogo de produtos precisa lidar com alto volume de leituras, enquanto o processamento de pagamentos lida com transações críticas em volume menor. Com microserviços, o catálogo pode ser otimizado para leitura e escalado horizontalmente, enquanto o serviço de pagamento pode priorizar segurança e consistência.

3. Evolução Tecnológica Gradual

Para sistemas legados que precisam evoluir:

  • Strangler Pattern: Permite substituir gradualmente partes de um sistema monolítico.

  • Adoção de novas tecnologias: Novos serviços podem usar tecnologias modernas sem reescrever todo o sistema.

  • Mitigação de risco: Mudanças são isoladas a serviços específicos, reduzindo o impacto de falhas.

Exemplo de código: Implementando um API Gateway para redirecionar gradualmente tráfego de um monolito para novos microserviços:

// Usando Ocelot como API Gateway em .NET
// ocelot.json
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/products/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "new-product-service",
          "Port": 443
        }
      ],
      "UpstreamPathTemplate": "/api/products/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ]
    },
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "https",
      "DownstreamHostAndPorts": [
        {
          "Host": "legacy-monolith",
          "Port": 443
        }
      ],
      "UpstreamPathTemplate": "/api/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ]
    }
  ]
}

O Meio-Termo: Monolito Modular

Uma abordagem que tem ganhado popularidade é o "monolito modular" - uma arquitetura que mantém as vantagens operacionais de um monolito, mas com a organização interna inspirada em microserviços.

Características do Monolito Modular:

  • Limites claros entre módulos: Cada módulo tem sua própria camada de domínio, aplicação e infraestrutura.

  • Comunicação via interfaces bem definidas: Módulos interagem através de interfaces ou eventos internos.

  • Possibilidade de extração futura: Módulos bem isolados podem ser extraídos como microserviços quando necessário.

Exemplo de estrutura em .NET:

Solution/
├── Modules/
│   ├── Catalog/
│   │   ├── Catalog.Domain/
│   │   ├── Catalog.Application/
│   │   └── Catalog.Infrastructure/
│   ├── Ordering/
│   │   ├── Ordering.Domain/
│   │   ├── Ordering.Application/
│   │   └── Ordering.Infrastructure/
│   └── ...
├── Shared/
│   ├── Shared.Domain/
│   └── Shared.Infrastructure/
└── Web/
    └── API/

Esta abordagem permite começar com a simplicidade de um monolito, mas com a estrutura interna que facilita uma eventual migração para microserviços, se e quando isso se tornar necessário.

Critérios para Tomar a Decisão

Para ajudar na decisão entre monolito e microserviços, considere os seguintes critérios:

Critérios Organizacionais:

  • Tamanho e estrutura da equipe: Equipes pequenas geralmente se beneficiam mais de monolitos; equipes grandes ou múltiplas equipes podem precisar da autonomia de microserviços.

  • Maturidade DevOps: Microserviços exigem práticas DevOps maduras, automação e monitoramento avançado.

  • Conhecimento técnico: Microserviços requerem conhecimento em sistemas distribuídos, mensageria, e padrões de resiliência.

Critérios Técnicos:

  • Requisitos de escala: Partes do sistema com necessidades de escala drasticamente diferentes podem justificar microserviços.

  • Isolamento de falhas: Se falhas em um componente não devem afetar outros, microserviços oferecem melhor isolamento.

  • Ciclos de release: Componentes que precisam ser atualizados em frequências muito diferentes podem se beneficiar de implantações independentes.

Critérios de Negócio:

  • Estágio do produto: Produtos em estágio inicial geralmente se beneficiam da simplicidade de monolitos.

  • Domínios de negócio: Domínios naturalmente separados com poucas dependências são candidatos a microserviços.

  • Velocidade vs. Estabilidade: Priorizar velocidade inicial favorece monolitos; priorizar estabilidade e escala a longo prazo pode favorecer microserviços.

Casos de Estudo: Migrações em Ambas Direções

É interessante notar que existem casos de empresas migrando em ambas direções:

De Monolito para Microserviços:

  • Netflix: Migrou de um monolito para microserviços para suportar seu crescimento global e necessidades de escala.

  • Amazon: Evoluiu de um monolito para uma arquitetura de serviços para permitir que equipes autônomas inovassem independentemente.

De Microserviços para Monolito (ou simplificação) :

  • Segment: Documentou sua jornada de voltar de microserviços para um monolito devido à complexidade operacional.

  • Istio: Simplificou sua arquitetura de microserviços para reduzir a complexidade e melhorar a experiência do desenvolvedor.

Estes exemplos mostram que não existe uma resposta universal - a melhor arquitetura depende do contexto específico e pode mudar com o tempo.

Conclusão: Uma Abordagem Pragmática

A escolha entre monolito e microserviços não deve ser baseada em tendências, mas em uma análise cuidadosa do contexto específico do seu projeto. Aqui estão algumas recomendações pragmáticas:

  1. Comece simples: Para novos projetos, um monolito bem estruturado geralmente é a escolha mais sensata.

  2. Projete para evolução: Mesmo em um monolito, adote princípios de design que facilitem uma eventual migração para microserviços, como modularidade e baixo acoplamento.

  3. Migre incrementalmente: Se decidir migrar para microserviços, faça isso gradualmente, começando pelos componentes que mais se beneficiariam da separação.

  4. Reavalie constantemente: As necessidades mudam com o tempo; o que era apropriado ontem pode não ser hoje.

Como desenvolvedor .NET, tenho visto muitas equipes adotarem microserviços prematuramente, apenas para descobrir que a complexidade adicional não era justificada pelos benefícios. Por outro lado, também vi monolitos que cresceram além do gerenciável e se beneficiaram enormemente da migração para microserviços.

A verdadeira habilidade de um arquiteto não está em seguir tendências, mas em escolher a ferramenta certa para o trabalho certo, no momento certo.


E você, qual tem sido sua experiência com monolitos e microserviços? Já passou por uma migração em alguma direção? Compartilhe nos comentários!


Referências:

  1. Martin Fowler - MonolithFirst

  2. Sam Newman - "Building Microservices" (O'Reilly)

  3. Microsoft - .NET Microservices Architecture

  4. Segment - Goodbye Microservices

  5. ThoughtWorks - Technology Radar

0
Subscribe to my newsletter

Read articles from Eduardo Cintra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Eduardo Cintra
Eduardo Cintra