Tipos de Módulos e seus Papéis no Sistema
03 de maio de 2026
EAE, bora tomar um cafezinho? ☕
No capítulo anterior, eu entrei em um ponto que considero decisivo para qualquer conversa séria sobre arquitetura modular: como estruturar módulos.
Ali, a ideia principal foi olhar para responsabilidade, domínio, capacidade de negócio, fluxo, granularidade, fronteiras e coerência interna. Não como uma receita pronta, mas como critérios para pensar melhor. Porque modularidade boa não nasce de uma pasta bem nomeada. Ela nasce de uma decisão estrutural que ajuda o sistema a continuar compreensível e seguro para evoluir.
Mas existe um passo importante depois disso.
Mesmo quando eu já entendo que módulos devem proteger responsabilidades, ainda preciso reconhecer uma coisa: nem todo módulo cumpre o mesmo papel dentro de um sistema.
Essa distinção parece simples, mas muda muita coisa.
Porque, na prática, é comum tratarmos todos os módulos como se fossem equivalentes. Como se um módulo de domínio, um módulo de aplicação, um módulo de infraestrutura, um módulo de integração, um módulo de interface e um módulo compartilhado estivessem todos no mesmo nível conceitual. Como se todos representassem o mesmo tipo de decisão arquitetural.
Só que não representam.
Alguns módulos protegem regras. Outros coordenam fluxos. Outros escondem detalhes técnicos. Outros traduzem conversas com sistemas externos. Outros recebem comandos de entrada. Outros concentram capacidades transversais.
Quando essa diferença não fica clara, a arquitetura começa a misturar coisas que deveriam ter papéis distintos. Regra de negócio aparece dentro de adapter. Detalhe de banco aparece no coração do domínio. Controller começa a decidir comportamento. Shared vira depósito de tudo. Integração externa passa a contaminar o modelo interno. E, aos poucos, o sistema até continua funcionando, mas perde nitidez.
É aqui que este capítulo entra.
Não para criar uma taxonomia rígida e universal de módulos. Eu não acredito que toda arquitetura precise ter exatamente as mesmas categorias, com os mesmos nomes, no mesmo formato. Isso seria trocar pensamento arquitetural por liturgia estrutural.
O objetivo aqui é outro: criar repertório.
Eu quero olhar para os principais papéis que módulos podem assumir dentro de uma arquitetura modular e entender o que cada um deles deveria proteger, o que deveria evitar e como eles se relacionam sem virar uma massa confusa de dependências.
Porque modularidade não é apenas separar.
É separar sabendo por quê.
Papel não é estética
Antes de falar dos tipos de módulos, eu preciso fazer uma distinção que parece pequena, mas é fundamental: o papel de um módulo não é definido pela estética da sua pasta.
Um módulo chamado domain não é necessariamente um módulo de domínio.
Um módulo chamado application não é automaticamente uma camada de aplicação bem desenhada.
Um pacote chamado shared não se torna seguro só porque parece reutilizável.
Uma pasta chamada infra não garante que detalhes técnicos estejam realmente isolados.
Nome ajuda. Estrutura ajuda. Convenção ajuda.
Mas nada disso substitui responsabilidade real.
O papel de um módulo é definido pelo tipo de decisão que ele concentra.
Se ele concentra regras essenciais do negócio, ele está próximo do domínio. Se ele coordena casos de uso, ele está próximo da aplicação. Se ele lida com banco, mensageria, cache, arquivos, APIs externas ou frameworks, ele está próximo da infraestrutura. Se ele conversa com outro sistema, traduzindo protocolos, dados e erros, ele está próximo de integração. Se ele recebe entrada de usuários, consumidores, jobs, eventos ou comandos externos, ele está próximo da interface. Se ele oferece algo comum a várias partes, ele pode ser compartilhado ou transversal — mas aí mora um risco que merece bastante cuidado.
Perceba que essa leitura não depende primeiro da pasta. Ela depende da função estrutural.
Essa inversão é importante.
Porque uma arquitetura pode estar visualmente organizada e conceitualmente confusa. E pode também, em alguns casos, ter uma estrutura simples, mas papéis muito bem preservados.
O que me interessa aqui não é fazer o sistema parecer arquiteturalmente sofisticado. É fazer com que cada parte tenha um motivo claro para existir.
Módulos de domínio
Eu gosto de começar pelos módulos de domínio porque eles costumam carregar o conhecimento mais importante do sistema.
Um módulo de domínio é aquele que protege conceitos, regras, invariantes e decisões essenciais do problema que o software resolve. Ele não existe para lidar com tecnologia. Ele existe para representar uma parte significativa do negócio ou da lógica central da aplicação.
Quando eu olho para um módulo de domínio, a pergunta não deveria ser: “qual framework ele usa?”. A pergunta deveria ser: qual conhecimento do negócio vive aqui?
Em um sistema de e-commerce, por exemplo, pode existir uma região conceitual relacionada a produto. Mas “produto” não precisa ser uma caixa única, ingênua e inflada. Dependendo do contexto, um módulo de produto pode concentrar responsabilidades internas diferentes: identidade do produto, atributos, categorização, publicação, variações, regras de disponibilidade, composição de catálogo, talvez precificação, talvez não.
E esse “talvez” importa muito.
Porque não existe uma regra universal dizendo que preço pertence sempre ao produto, ou que estoque pertence sempre ao produto, ou que catálogo deve sempre ser separado de produto. O recorte depende do domínio, da complexidade, dos motivos de mudança e da linguagem real do sistema.
Em alguns contextos, preço pode ser apenas uma informação simples do produto. Em outros, precificação pode ser uma capacidade complexa, com políticas, campanhas, regiões, canais, regras comerciais e histórico. Em alguns contextos, estoque pode ser só uma quantidade exibida. Em outros, estoque pode ser um domínio próprio, com reserva, movimentação, disponibilidade, armazém e conciliação.
É por isso que eu tenho cuidado com exemplos. Eles ajudam, mas também podem viciar a leitura.
A ideia principal é esta: um módulo de domínio deve proteger aquilo que não deveria depender de detalhes externos para fazer sentido.
Se uma regra é essencial para a validade de um produto, de um pedido, de uma cobrança, de um contrato ou de uma publicação, ela não deveria estar espalhada em controllers, adapters, queries soltas ou integrações externas. Ela deveria estar próxima do modelo que representa aquela responsabilidade.
Isso não significa que todo domínio precise ser rico, complexo ou cheio de abstrações. Às vezes, a regra é simples. Às vezes, o domínio ainda está amadurecendo. Às vezes, uma estrutura mais direta é suficiente.
Mas quando existe regra importante, ela precisa de lugar.
E esse lugar não deveria ser escolhido por conveniência técnica.
Um bom módulo de domínio ajuda o sistema a responder perguntas como:
o que torna este conceito válido?
quais regras não podem ser violadas?
quais decisões pertencem a esta parte do negócio?
quais mudanças deveriam acontecer aqui dentro?
quais detalhes externos não deveriam contaminar este modelo?
Essa última pergunta é decisiva.
Porque domínio contaminado por infraestrutura começa a perder independência conceitual. O modelo passa a saber demais sobre banco, APIs, formatos externos, filas, frameworks, protocolos e detalhes que deveriam estar nas bordas. Quando isso acontece, o coração do sistema fica menos livre para evoluir.
E uma arquitetura modular séria tenta evitar exatamente esse tipo de captura.
Módulos de aplicação
Se o domínio concentra regras e decisões essenciais, os módulos de aplicação costumam concentrar a coordenação dos casos de uso.
Eles respondem a outra pergunta.
Não é apenas “qual regra vale?”. É: como esta intenção do sistema deve ser executada?
Um módulo de aplicação normalmente orquestra passos. Ele recebe uma intenção, coordena objetos de domínio, chama portas ou serviços necessários, lida com transação, aciona integrações por meio de abstrações, monta respostas e preserva o fluxo do caso de uso.
Ele não deveria ser o lugar onde regras fundamentais são inventadas sem critério.
Essa diferença é importante porque muita arquitetura degrada exatamente aqui. O módulo de aplicação começa como coordenador e, pouco a pouco, vira o lugar onde tudo acontece. Toda regra fica em service. Toda decisão fica no caso de uso. Toda validação importante vira if procedural espalhado. O domínio fica anêmico, a aplicação fica inchada e o sistema perde uma separação que poderia ajudar muito.
Claro, aqui também não vale dogma.
Nem toda aplicação precisa de um domínio rico. Nem toda regra precisa virar objeto elaborado. Nem todo service com lógica é automaticamente um problema. Existem sistemas simples, CRUDs administrativos, fluxos diretos e domínios pouco complexos em que uma aplicação mais procedural pode ser suficiente.
O problema não é existir lógica na aplicação.
O problema é a aplicação começar a concentrar decisões que pertencem a responsabilidades mais profundas e duráveis do sistema.
Um módulo de aplicação saudável costuma ter uma característica boa: ele é explícito sobre intenção.
Criar produto. Publicar produto. Alterar preço. Inativar oferta. Processar pedido. Cancelar contrato. Aprovar solicitação. Recalcular disponibilidade.
Esses nomes representam ações do sistema. E ações precisam de coordenação.
Mas coordenação não é a mesma coisa que domínio.
Quando a aplicação coordena bem, ela deixa claro o fluxo sem absorver indevidamente o conhecimento que deveria estar protegido em módulos de domínio.
Ela sabe chamar. Sabe ordenar. Sabe combinar. Sabe acionar. Sabe persistir por meio de abstrações. Sabe devolver uma resposta coerente.
Mas ela não deveria se tornar o único lugar onde o negócio existe.
Módulos de infraestrutura
Módulos de infraestrutura lidam com detalhes técnicos.
E aqui a palavra “detalhe” não significa irrelevância. Banco de dados é importante. Mensageria é importante. Cache é importante. Observabilidade é importante. Cliente HTTP é importante. Framework é importante. Sem essas coisas, o sistema real não opera.
Mas, arquiteturalmente, elas são mecanismos.
Elas viabilizam o funcionamento do sistema, mas não deveriam definir o significado central do domínio.
Um módulo de infraestrutura pode conter implementações de repositórios, clientes de banco, adaptadores de fila, configurações técnicas, serializadores, mecanismos de cache, integração com storage, providers de autenticação, ferramentas de observabilidade e outras peças que conectam a aplicação ao mundo concreto.
O papel dele é esconder complexidade técnica atrás de uma fronteira mais estável.
Quando isso funciona bem, o restante do sistema não precisa conhecer detalhes de implementação para expressar uma intenção. A aplicação não precisa saber exatamente como uma query foi escrita. O domínio não precisa saber qual banco está sendo usado. Um caso de uso não deveria depender diretamente do formato específico de uma API externa se existe uma abstração adequada protegendo essa conversa.
Mas aqui existe uma tentação grande: colocar regra de negócio na infraestrutura porque “já está perto dos dados”.
Isso acontece muito.
Uma query começa filtrando registros. Depois ganha uma regra. Depois ganha uma exceção. Depois calcula um estado. Depois decide uma política. Depois vira o lugar real onde o comportamento mora.
E o problema é que isso parece eficiente no começo.
Está perto do banco. Está perto do retorno. Está perto da integração. Está fácil de alterar.
Só que, aos poucos, o comportamento essencial do sistema fica escondido em lugares técnicos. A regra deixa de ser expressa como parte do domínio ou da aplicação e passa a morar em detalhes de persistência, adapters ou consultas específicas.
Quando isso acontece, a arquitetura perde transparência.
Não porque infraestrutura seja ruim. Mas porque infraestrutura passou a fazer um papel que não era dela.
Um bom módulo de infraestrutura deve permitir que detalhes técnicos mudem com o menor impacto possível sobre o restante do sistema. Trocar uma estratégia de persistência, ajustar um client externo, refinar cache, alterar serialização ou mudar um mecanismo de mensageria não deveria exigir reescrever o entendimento central do negócio.
Esse é um dos motivos pelos quais separar papéis importa.
Não é purismo. É preservar liberdade de evolução.
Módulos de integração
Módulos de integração merecem uma atenção própria porque eles lidam com uma fronteira muito sensível: a conversa com outros sistemas.
E conversar com outros sistemas nunca é neutro.
Cada sistema externo tem seu vocabulário, seus formatos, suas limitações, seus códigos de erro, seus estados, suas instabilidades, suas regras implícitas e suas mudanças inesperadas. Se essa realidade entra sem tradução no coração da aplicação, o modelo interno começa a ser moldado por decisões que não pertencem a ele.
Um módulo de integração deveria funcionar como uma região de tradução e proteção.
Ele conhece a API externa. Conhece o contrato externo. Conhece o payload externo. Conhece os erros externos. Conhece os detalhes de autenticação, timeout, retry, headers, formatos, versionamento e comportamento daquela dependência.
Mas ele deveria evitar que tudo isso vazasse para dentro do domínio.
Esse ponto é muito importante.
Se o sistema externo chama uma coisa de sku_id, isso não significa que o domínio interno precise adotar o mesmo nome.
Se a API externa retorna um status estranho, isso não significa que esse status deva virar estado central do seu modelo.
Se um provedor externo exige um formato específico, isso não significa que sua aplicação inteira deva passar a falar naquele formato.
Integração boa traduz.
Ela não apenas repassa.
É claro que a tradução tem custo. Criar adapters, mapeamentos, contratos internos e mecanismos de isolamento exige trabalho. Mas esse trabalho pode proteger o sistema de um acoplamento muito mais caro: o acoplamento semântico com terceiros.
E esse tipo de acoplamento é perigoso porque parece apenas técnico.
Mas não é.
Quando um sistema externo começa a definir seus nomes, seus estados, seus fluxos e suas exceções internas, ele deixou de ser apenas uma dependência técnica. Ele virou uma força modeladora dentro da sua arquitetura.
Às vezes isso é aceitável. Às vezes o sistema externo é realmente dominante no contexto. Mas essa decisão precisa ser consciente.
Um módulo de integração ajuda justamente a tornar essa fronteira visível.
Módulos de interface
Módulos de interface são portas de entrada e, às vezes, de saída da aplicação.
Eles podem aparecer como controllers HTTP, resolvers GraphQL, consumidores de eventos, handlers de fila, CLIs, telas, jobs agendados, endpoints internos, webhooks ou qualquer mecanismo que receba estímulos externos e transforme isso em uma intenção compreensível para o sistema.
O papel da interface não é carregar o negócio inteiro.
O papel dela é adaptar a entrada.
Ela recebe uma requisição, valida aspectos superficiais, traduz parâmetros, lida com protocolo, autenticação de borda quando aplicável, formatação de resposta, códigos HTTP, eventos recebidos, comandos externos e detalhes da experiência de consumo.
Mas a interface deveria ser cuidadosa para não virar o cérebro do sistema.
Quando regra importante começa a morar na interface, o comportamento fica preso ao canal de entrada. A regra que vale para uma API talvez precise valer também para um job, para um evento, para uma rotina interna ou para outro tipo de entrada. Se ela está escondida em um controller, o sistema começa a duplicar comportamento ou criar caminhos inconsistentes.
Esse é um sintoma comum.
A API valida de um jeito. O job valida de outro. O consumidor de evento ignora uma regra. A tela aplica uma restrição que o backend não garante. O endpoint novo esquece uma condição que estava no endpoint antigo.
Quando isso acontece, o problema não é apenas duplicação. É falta de centralidade da regra.
Um módulo de interface saudável deveria ser relativamente fino em decisão de negócio. Ele pode ter validações de formato, permissões de entrada, montagem de request, tratamento de protocolo e tradução de resposta. Mas decisões centrais deveriam ser encaminhadas para aplicação e domínio.
Essa separação não existe para deixar o código mais bonito.
Ela existe para impedir que o comportamento do sistema dependa do caminho por onde ele foi acionado.
Módulos compartilhados e transversais
Agora chegamos em uma das partes mais delicadas: módulos compartilhados.
Poucas coisas parecem tão úteis no começo e tão perigosas com o tempo quanto um shared mal governado.
A ideia parece ótima: colocar coisas comuns em um lugar comum. Evitar duplicação. Reutilizar componentes. Padronizar estruturas. Facilitar o desenvolvimento.
E, em alguns casos, isso faz sentido.
Um módulo compartilhado pode ser útil para conceitos realmente estáveis, utilitários pequenos, abstrações técnicas genéricas, tipos comuns de erro, mecanismos de observabilidade, helpers de configuração, contratos extremamente transversais ou capacidades que atravessam várias partes do sistema sem pertencer a um domínio específico.
Mas o problema começa quando shared vira terra sem lei.
Tudo que não tem lugar vai para shared. Tudo que duas partes usam vai para shared. Toda regra duplicada vai para shared. Todo DTO reaproveitado vai para shared. Toda classe “útil” vai para shared. Toda decisão difícil vai para shared.
E, quando percebemos, o shared virou o módulo mais importante do sistema.
Só que ele não tem responsabilidade clara.
Esse é o perigo.
Um shared gigante não é sinal de maturidade modular. Muitas vezes é sinal de fronteira fraca. É o lugar onde a arquitetura coloca aquilo que ainda não conseguiu entender.
Nem toda duplicação é pior do que reuso.
Essa frase pode soar estranha, mas é importante.
Duplicação localizada, pequena e consciente pode ser menos nociva do que um compartilhamento prematuro que cria acoplamento entre módulos que deveriam evoluir separadamente. Quando dois módulos usam a mesma classe compartilhada, eles passam a depender da mesma decisão. Se essa decisão mudar por causa de um módulo, o outro pode ser afetado sem realmente participar daquele motivo de mudança.
Isso é acoplamento disfarçado de reaproveitamento.
Por isso, antes de mover algo para shared, eu gosto de perguntar:
isso é realmente comum ou apenas parecido?
essa abstração é estável ou ainda está mudando?
os módulos que usam isso mudam pelos mesmos motivos?
esse compartilhamento reduz complexidade ou espalha dependência?
quem é dono dessa decisão compartilhada?
A última pergunta é essencial.
Shared sem ownership vira depósito. E depósito cresce rápido.
Módulos transversais também precisam de cuidado. Autenticação, autorização, auditoria, observabilidade, notificações, configuração e políticas globais podem atravessar o sistema inteiro. Mas atravessar o sistema não significa poder invadir qualquer fronteira sem critério.
Preocupações transversais precisam ser desenhadas de modo que apoiem os módulos, não que dissolvam suas responsabilidades.
Um módulo pode ter módulos internos
Aqui entra um ponto que vale reforçar com calma: quando eu falo de tipos de módulos, não estou dizendo que o sistema inteiro precisa ser uma lista plana dessas categorias.
Um módulo maior pode ter módulos internos.
E isso é especialmente importante quando pensamos em algo como um módulo de produto.
Dependendo do sistema, “produto” pode ser apenas uma entidade simples. Mas, em outro contexto, produto pode carregar uma complexidade grande o suficiente para justificar submódulos de responsabilidade. Posso ter uma região voltada para identidade do produto, outra para atributos e especificações, outra para categorização, outra para publicação, outra para políticas de exibição, outra para integração com busca, outra para imagens, outra para precificação — se, e somente se, essas responsabilidades tiverem peso real naquele domínio.
Atenção ao “se”.
Não é porque eu consigo dividir que eu devo dividir.
Essa é uma armadilha recorrente. A gente aprende uma boa ideia arquitetural e começa a enxergá-la em todos os lugares. De repente, todo conceito vira contexto, todo agrupamento vira módulo, toda pasta vira fronteira, toda classe comum vira shared e toda diferença vira motivo para separação.
Isso não é modularidade madura.
É ansiedade estrutural.
Um módulo de produto pode ter submódulos organizados por contexto ou responsabilidade quando isso melhora a compreensão e a evolução. Mas também pode ser mais simples quando a complexidade ainda não justifica essa separação. O que define o desenho não é a vontade de parecer sofisticado. É o peso real das decisões que precisam ser protegidas.
Em um sistema mais maduro, talvez faça sentido pensar algo como:
O núcleo conceitual de produto protege identidade, validade e regras essenciais. A aplicação coordena casos de uso como criar, publicar, alterar, inativar ou revisar produto. A infraestrutura implementa persistência, busca, cache e leitura otimizada. A integração conversa com serviços externos, catálogo legado, mídia, estoque ou mecanismos de indexação. A interface recebe comandos de API, eventos, painel administrativo ou jobs.
Tudo isso pode existir dentro de um recorte maior chamado produto.
Mas também pode ser separado em módulos próprios se o domínio pedir.
Essa é a parte que exige discernimento.
Produto pode ser um módulo. Produto pode ser um contexto. Produto pode ser uma entidade dentro de catálogo. Produto pode ser uma capacidade dentro de comercial. Produto pode ser pequeno demais para justificar uma fronteira própria. Produto pode ser grande demais para continuar como uma única caixa.
Não existe resposta universal.
Existe leitura do contexto.
E essa leitura precisa considerar responsabilidade, mudança, linguagem, complexidade, dependência, ownership e custo de fronteira.
O risco de misturar papéis
Boa parte dos problemas em arquitetura modular aparece quando os papéis começam a se misturar sem intenção clara.
Um pouco de mistura é inevitável. Sistemas reais não são diagramas perfeitos. Há decisões pragmáticas, atalhos conscientes, limitações de prazo, legados, exceções e trade-offs. O problema não é qualquer mistura pontual. O problema é a mistura virar modelo dominante sem ninguém perceber.
Quando o domínio conhece detalhes de infraestrutura, ele perde liberdade conceitual. Quando a aplicação absorve toda regra, ela vira um centro procedural inchado. Quando a interface decide comportamento essencial, cada entrada passa a ter sua própria versão da verdade. Quando integração externa vaza para dentro do modelo, o sistema passa a falar a língua de terceiros. Quando shared recebe tudo que parece comum, ele se torna uma dependência global difícil de mudar. Quando infraestrutura concentra regra porque está perto dos dados, a lógica fica escondida onde deveria haver mecanismo.
Esses problemas não costumam aparecer de forma dramática no primeiro dia.
Eles aparecem como pequenas facilidades.
“Coloca essa regra aqui rapidinho.” “Reaproveita esse DTO mesmo.” “Usa essa classe do outro módulo só dessa vez.” “Joga no shared depois a gente organiza.” “Faz no controller porque é só uma validação.” “Resolve na query porque fica mais performático.” “Mapeia direto o retorno da API externa porque é mais simples.”
Cada uma dessas frases pode até ter uma justificativa local.
Mas arquitetura se degrada por acúmulo.
E o acúmulo de exceções sem direção transforma papéis claros em fronteiras borradas.
Por isso, entender tipos de módulos não é uma questão acadêmica. É uma forma de detectar erosão antes que ela vire normalidade.
Como reconhecer o papel de um módulo
Uma forma prática de analisar um módulo é parar de olhar primeiro para o nome dele e começar a olhar para as decisões que ele toma.
Eu perguntaria:
este módulo protege regra de negócio ou coordena um caso de uso?
ele representa conhecimento do domínio ou detalhe técnico?
ele conversa com sistemas externos ou expressa o modelo interno?
ele recebe entrada ou executa comportamento central?
ele é compartilhado porque é realmente transversal ou porque ninguém soube onde colocar?
ele pode mudar internamente sem obrigar outros módulos a mudarem junto?
ele depende de detalhes de outros módulos ou apenas de contratos necessários?
Essas perguntas ajudam a revelar o papel real.
Às vezes, um módulo chamado product-service parece ser aplicação, mas concentra domínio, infraestrutura, integração, validação de interface e regras compartilhadas. Ele é tudo ao mesmo tempo. E, quando um módulo é tudo ao mesmo tempo, ele começa a perder identidade.
Às vezes, um shared parece pequeno, mas carrega decisões de domínio que pertencem a contextos específicos.
Às vezes, um adapter externo parece técnico, mas está decidindo política de negócio.
Às vezes, uma camada de interface parece fina, mas contém regras que deveriam valer em qualquer canal.
Às vezes, um módulo de domínio parece protegido, mas depende diretamente de estruturas de persistência.
A arquitetura real aparece nesses detalhes.
Não no diagrama bonito.
O diagrama pode ajudar, claro. Mas o código mostra onde as decisões realmente moram.
Papéis claros melhoram a conversa arquitetural
Uma vantagem importante de diferenciar tipos de módulos é que isso melhora a conversa do time.
Quando alguém diz “vamos criar um módulo para isso”, a pergunta seguinte pode ser mais precisa:
Que tipo de módulo? Um módulo de domínio? Um módulo de aplicação? Um adapter de integração? Uma interface de entrada? Uma capacidade transversal? Um shared? Um submódulo dentro de um contexto maior?
Essa clareza evita discussões vagas.
Porque “criar um módulo” pode significar coisas muito diferentes. Pode significar proteger uma regra. Pode significar isolar uma dependência externa. Pode significar organizar casos de uso. Pode significar evitar que um detalhe técnico se espalhe. Pode significar estabelecer uma fronteira de ownership. Pode significar apenas criar uma pasta.
E essas decisões não têm o mesmo peso.
Quando o time entende os papéis, ele começa a discutir arquitetura com mais precisão. Em vez de debater só nomes e estrutura visual, passa a debater responsabilidade, dependência, mudança, contrato e impacto.
Isso muda o nível da conversa.
E arquitetura modular precisa desse nível.
Porque, se a equipe não consegue explicar o papel de um módulo, provavelmente terá dificuldade para preservar sua fronteira com o tempo.
Um cuidado contra taxonomias rígidas
Depois de falar sobre módulos de domínio, aplicação, infraestrutura, integração, interface e shared, eu preciso colocar um freio.
Essa taxonomia é útil, mas não deve virar prisão.
Nem todo sistema precisa materializar todos esses papéis como módulos separados. Nem todo módulo precisa ter a mesma estrutura interna. Nem toda responsabilidade precisa virar fronteira explícita. Nem toda diferença conceitual justifica separação física. Nem todo compartilhamento é ruim. Nem toda regra em aplicação é erro. Nem toda infraestrutura mais próxima do caso de uso é um problema.
O que importa é a consciência da decisão.
Arquitetura modular não melhora porque o sistema ficou cheio de caixas. Ela melhora quando as caixas representam responsabilidades reais, papéis compreensíveis e fronteiras que reduzem confusão em vez de aumentá-la.
Às vezes, separar demais custa caro. Às vezes, juntar demais custa caro. Às vezes, o módulo precisa amadurecer antes de ser dividido. Às vezes, a divisão precisa acontecer antes que o acoplamento endureça. Às vezes, o melhor desenho hoje não será o melhor daqui a seis meses.
Isso faz parte.
Por isso, eu prefiro tratar esses tipos de módulos como lentes de análise, não como formulário obrigatório.
A pergunta não é: “meu sistema tem todos esses módulos?”. A pergunta é: as responsabilidades do meu sistema estão cumprindo papéis claros ou estão misturadas de um jeito que dificulta evolução?
Essa pergunta é bem mais valiosa.
Fechamento
Se eu tivesse que resumir este capítulo em uma ideia central, seria esta: módulos não são todos iguais, porque responsabilidades não são todas do mesmo tipo.
Alguns módulos protegem conhecimento de domínio. Outros coordenam intenções do sistema. Outros escondem mecanismos técnicos. Outros traduzem integrações externas. Outros recebem comandos e adaptam entradas. Outros oferecem capacidades compartilhadas ou transversais.
Quando esses papéis ficam claros, a arquitetura ganha nitidez. Fica mais fácil entender onde uma regra deve morar, onde uma decisão técnica deve ficar escondida, onde uma integração deve ser traduzida, onde uma entrada deve ser adaptada e quando um compartilhamento começa a virar acoplamento disfarçado.
Isso não resolve tudo.
Mas melhora muito a qualidade das perguntas.
E arquitetura, no fim das contas, depende bastante das perguntas certas.
Não basta perguntar: “em qual pasta eu coloco isso?”. Também não basta perguntar: “qual nome fica mais bonito?”. A pergunta mais importante é: que papel essa parte cumpre no sistema e qual responsabilidade ela precisa proteger?
Quando essa resposta é clara, o módulo começa a ter identidade. Quando ela é confusa, a estrutura pode até parecer modular, mas a arquitetura ainda está nebulosa.
Eu gosto de encerrar este quinto movimento exatamente aqui porque agora já temos algo importante nas mãos. Primeiro, entendemos por que modularidade importa. Depois, vimos princípios. Em seguida, falamos sobre benefícios, objetivos e critérios de estruturação. Agora, distinguimos os papéis que diferentes módulos podem assumir dentro do sistema.
O próximo passo é inevitável.
Se módulos têm papéis diferentes, eles precisam se comunicar sem perder suas fronteiras. Eles precisam expor contratos, esconder detalhes internos, controlar dependências e impedir que uma parte do sistema atravesse a outra de qualquer jeito.
Porque, no fim, modularidade não vive apenas dentro dos módulos.
Ela vive principalmente entre eles.