Pular para o conteúdo
ZYNQORALABS

Monólito modular antes de microsserviços

Microsserviços resolvem problemas de escala e de organização, e criam outros de rede e consistência. Como estruturar um monólito modular que pode ser dividido depois.

Por ZynqoraLabs · · 3 min de leitura

CompartilharLinkedInXWhatsApp

Muitos projetos começam com microsserviços porque parece a arquitetura "certa" para crescer. Meses depois, o time passa mais tempo depurando chamadas entre serviços, versões incompatíveis de contratos e transações que ficaram pela metade do que desenvolvendo funcionalidades.

Microsserviços resolvem problemas reais. Mas são problemas específicos, e a conta chega antes dos benefícios. Na maioria dos sistemas novos, começamos com um monólito modular: uma única aplicação implantável, dividida internamente em módulos com fronteiras tão rígidas quanto as de serviços separados.

O que microsserviços cobram

Separar um sistema em serviços transforma chamadas de função em chamadas de rede. Com isso vêm:

  • Falhas parciais. Um serviço responde, outro não. Cada chamada precisa de timeout, retentativa e um plano para quando falha.
  • Consistência distribuída. Uma operação que antes cabia em uma transação de banco agora atravessa serviços e exige sagas, outbox e compensações.
  • Contratos versionados. Mudar a forma de um dado exige coordenar versões entre times e implantações.
  • Operação. Mais pipelines, mais monitoramento, rastreamento distribuído para entender uma única requisição.

Esses custos compensam quando existe um motivo concreto: partes do sistema com necessidades de escala muito diferentes, times independentes que precisam implantar sem coordenação, ou requisitos de isolamento de falhas e de segurança.

O que é um monólito modular

Um monólito modular é uma aplicação organizada em módulos de domínio (pedidos, faturamento, catálogo, identidade) com três regras:

  1. Cada módulo expõe uma interface pública e esconde o resto. Outros módulos só chamam essa interface.
  2. Cada módulo é dono dos seus dados. Nenhum módulo lê ou escreve diretamente as tabelas de outro, mesmo estando no mesmo banco.
  3. As dependências entre módulos são explícitas e verificadas por ferramenta, não por convenção.
src/modules/
  orders/
    index.ts          ← interface pública do módulo
    internal/         ← regras, repositórios, tabelas: invisível aos outros módulos
  billing/
    index.ts
    internal/
  catalog/
    index.ts
    internal/

Fronteiras verificadas por ferramenta

Convenções se perdem com o tempo e com a pressão de prazo. Uma regra no CI que falha quando um módulo importa o interior de outro mantém a fronteira de verdade.

.dependency-cruiser.cjs
module.exports = {
  forbidden: [
    {
      name: "no-reaching-into-other-modules",
      comment: "Modules talk to each other only through their public index.ts",
      severity: "error",
      from: { path: "^src/modules/([^/]+)/" },
      to: {
        path: "^src/modules/([^/]+)/internal/",
        pathNot: "^src/modules/$1/internal/",
      },
    },
  ],
};

O mesmo vale para o banco: cada módulo usa seu próprio esquema (por exemplo, orders.* e billing.* no PostgreSQL), e as credenciais de cada módulo podem ser restritas ao próprio esquema.

Comunicação entre módulos

Dentro do monólito, módulos podem se comunicar de duas formas, e escolher bem facilita uma futura extração:

  • Chamadas síncronas pela interface pública, quando o chamador precisa da resposta para continuar.
  • Eventos de domínio publicados por um módulo e consumidos por outros, quando a reação pode acontecer depois. Por exemplo, pedido.criado faz o faturamento gerar uma cobrança.

Se os eventos já passam por uma outbox e são tratados de forma idempotente, transformá-los em mensagens de um broker no dia da extração é uma mudança de transporte, não de arquitetura.

Quando extrair um serviço

Com fronteiras bem mantidas, extrair um módulo vira uma decisão técnica, e não uma reescrita. Bons motivos:

  • O módulo tem um perfil de carga muito diferente do resto (processamento pesado em lote, picos de tráfego isolados).
  • Um time precisa implantar com uma frequência que o resto do sistema não acompanha.
  • O módulo exige isolamento: dados sensíveis, requisitos regulatórios, dependências com riscos próprios.

Motivos ruins: "microsserviços escalam melhor", sem uma medição que mostre o gargalo, ou "o código está bagunçado". Um monólito bagunçado vira microsserviços bagunçados, só que com rede no meio.

Resumo

Um monólito modular entrega a maior parte dos benefícios de organização dos microsserviços, como fronteiras claras, propriedade de dados e baixo acoplamento, sem os custos de rede e de consistência distribuída. Mantendo as fronteiras verificadas por ferramenta e a comunicação por interfaces e eventos, extrair um serviço no futuro é uma operação planejada, feita quando existe um motivo mensurável.