PEDRO OS
← Artigos

RELATÓRIO DE CAMPO · CODEX

O módulo que sabe demais não é um módulo. É um monolito com arquivos extras.

Modularizar um projeto sem modularizar o pensamento cria uma ilusão de isolamento. O resultado prático é um monolito distribuído em pastas, com acoplamento invisível e builds cada vez mais lentos.

O módulo que sabe demais não é um módulo. É um monolito com arquivos extras.

Eu já entreguei uma modularização que tecnicamente funcionava. Dividimos o projeto em módulos, configuramos as dependências, o build compilava por partes, o time ficou satisfeito. Parecia certo.

Seis meses depois, qualquer mudança no UserProfile quebrava quatro módulos que teoricamente não deveriam saber da existência um do outro.

Não era um problema de implementação. Era um problema de pensamento.


A ilusão do módulo

Quando a maioria dos times decide modularizar, o critério inicial é quase sempre topográfico: separa por feature, por layer, por domínio. O projeto cresce, o build fica lento, alguém sugere modularização, e a equipe começa a mover arquivos.

O resultado costuma ser o mesmo: os módulos existem no sistema de arquivos, mas as dependências continuam fluindo livremente pelo código.

Eu vi isso num projeto de fintech. O time tinha dezoito módulos. Mas o módulo de Authentication importava o módulo de UserProfile, que importava o módulo de Analytics, que importava o módulo de Core, que importava quase tudo. Qualquer mudança de modelo atravessava o grafo inteiro. O build passou de quatro minutos para onze em três meses. Não por causa do crescimento do código. Por causa do crescimento das dependências invisíveis.

A modularização estava lá. O isolamento, não.


O problema real não é técnico

A maioria das modularizações falha por um motivo que não aparece no diagrama de arquitetura: o time modularizou o projeto, mas não modularizou o domínio.

Cada módulo continua pensando no sistema inteiro. Cada feature sabe de detalhes que não deveria saber. Cada camada carrega contexto que não é dela.

Quando um módulo de Pagamentos precisa saber o nome do usuário logado, e vai buscar essa informação diretamente num modelo compartilhado de Auth, ele criou uma dependência de domínio que nenhuma fronteira técnica vai apagar.

Você pode colocar esse código em módulos separados, com targets separados, frameworks separados, repositórios separados. A dependência ainda existe. Ela só ficou menos visível.

Isso é o que eu chamo de Modularização Decorativa: a estrutura existe para organizar arquivos, não para isolar responsabilidades.


Por que Clean Architecture não resolve sozinha

Eu também pensei que Clean Architecture resolveria isso.

Se cada módulo seguir a regra das dependências, se cada camada apontar só para dentro, o isolamento vem junto. Pelo menos era o que eu acreditava quando estruturei o módulo de conta bancária num projeto de arquitetura de soluções.

O que aconteceu na prática: cada módulo tinha sua própria Clean Architecture interna, mas os módulos conversavam entre si através dos modelos de domínio. BankAccount, Transaction, UserProfile circulavam por tudo. Eram compartilhados via um módulo de Core que cresceu sem parar.

A Clean Architecture estava correta dentro de cada módulo. Mas entre os módulos, o acoplamento era total. A fronteira arquitetural existia verticalmente, não horizontalmente.

O Core virou o novo monolito.


O que muda quando você pensa em fronteiras antes de módulos

A virada de chave real não é decidir quantos módulos criar. É decidir o que cada módulo tem direito de saber.

Antes de criar qualquer target ou package, a pergunta certa é: quais são as fronteiras de conhecimento do sistema?

Um módulo de Pagamentos tem o direito de saber sobre fluxo de pagamento, métodos aceitos, estados de transação. Ele não tem o direito de saber sobre o perfil completo do usuário, sobre configurações de notificação, sobre o histórico de navegação.

Quando esse limite está claro no domínio, a estrutura de módulos emerge naturalmente. E mais importante: quando uma dependência viola esse limite, ela aparece como um cheiro ruim antes de virar um problema de build.

Na prática, isso muda como você define as interfaces entre módulos. Em vez de passar um modelo rico, você passa exatamente o que aquele módulo precisa saber. Um PayerID em vez de um UserProfile. Um AccountSummary em vez de um BankAccount completo.

É uma diferença pequena no código. É uma diferença enorme na manutenção.


O custo real de ignorar isso

Um sistema com Modularização Decorativa tem um comportamento previsível ao longo do tempo.

No começo, parece que a estrutura funciona. Os módulos existem, o time se orienta por eles, as features são desenvolvidas em paralelo.

Com seis meses, as dependências implícitas começam a aparecer. Um modelo compartilhado muda e o impacto é desproporcional. O build começa a recompilar módulos que não deveriam ser afetados.

Com um ano, ninguém mais consegue estimar o impacto de uma mudança no Core. A velocidade de entrega cai. O time começa a ter medo de mexer em código que "funciona".

Eu vi esse ciclo acontecer mais de uma vez. O ponto de inflexão é sempre o mesmo: o momento em que o time percebe que a modularização criou mais cerimônia sem criar mais isolamento.


O que eu faria diferente

Primeiro: nunca começaria pelos targets. Começaria pelo mapeamento de domínio. Quais são os contextos do sistema? Quais informações pertencem a cada contexto? Onde estão as fronteiras naturais?

Segundo: trataria o módulo de Core como um sinal de alerta. Toda vez que algo vai para o Core "porque é compartilhado", é uma decisão de domínio sendo adiada. Na maioria dos casos, aquele código pertence a um contexto específico. O Core cresce porque é fácil, não porque é correto.

Terceiro: definiria as interfaces entre módulos antes de definir a estrutura interna. O contrato externo de um módulo é mais importante que sua arquitetura interna. Um módulo com interface bem definida pode ter o interior refatorado a qualquer momento. Um módulo com interface porosa contamina tudo ao redor.


O que fica

Modularização é uma ferramenta de isolamento. Quando ela não isola nada, é só organização de arquivos com overhead de build.

A pergunta que vale fazer em qualquer projeto modularizado não é "quantos módulos temos?" É "o que cada módulo tem direito de ignorar?"

Um módulo que sabe demais não está bem encapsulado. Está bem localizado. São coisas muito diferentes.

E a diferença entre as duas costuma aparecer exatamente quando você menos quer: num refactor urgente, numa mudança de modelo de dados, numa integração nova que precisa entrar rápido.

É aí que você descobre se modularizou o projeto ou se modularizou o pensamento.