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:
- Cada módulo expõe uma interface pública e esconde o resto. Outros módulos só chamam essa interface.
- Cada módulo é dono dos seus dados. Nenhum módulo lê ou escreve diretamente as tabelas de outro, mesmo estando no mesmo banco.
- 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.
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.criadofaz 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.
Leituras relacionadas
- Arquitetura de software · 3 min de leitura
- Engenharia de backend · 3 min de leitura
- Engenharia de backend · 3 min de leitura