Quanto maior o projeto, mais assustador fica alterar um único arquivo. Afinal, não dá para estimar até onde o impacto vai se espalhar.
O build fica mais lento e o escopo do code review aumenta. Quem acabou de entrar no time também não sabe por onde começar a leitura.
A modularização é a solução mais antiga para esse problema. Em uma frase: é dividir grandes blocos de código em unidades que possam ser entendidas e substituídas de forma independente.
Neste artigo, resumimos o que a modularização realmente divide, os critérios de um bom módulo — alta coesão e baixo acoplamento — e quando fazer essa divisão.
Vamos começar pelo resumo.
- Modularização é agrupar o código que «muda junto» e estabelecer limites.
- Um bom módulo tem alta coesão e baixo acoplamento.
- O objetivo principal é reduzir o custo de mudança, não acelerar o build.
- Dividir sem limites claros só aumenta a complexidade.
O que exatamente a modularização divide?
Organizar pastas e dividir módulos são coisas diferentes.
Uma pasta apenas organiza arquivos. O código de qualquer pasta pode usar livremente o código de outra.
Um módulo cria um limite obrigatório. De fora, só é possível usar a interface que o módulo declarou como pública (public).
A essência da modularização não é organizar arquivos, mas fazer o compilador impor o que precisa ser conhecido externamente e o que pode permanecer oculto.
Esse limite possibilita duas coisas.
- Compreensão independente: basta olhar a interface para usar o módulo, sem conhecer seu interior.
- Substituição independente: se a interface for a mesma, é possível trocar toda a implementação sem afetar o exterior.
Em 1972, David Parnas apresentou em um artigo o conceito de ocultação de informação (information hiding). O critério para dividir módulos não são as funcionalidades, mas as «decisões de design que queremos ocultar».
Critérios de um bom módulo: coesão e acoplamento
Se dividir os módulos os tornou menos práticos, geralmente essas duas métricas estão desalinhadas.
Coesão indica o quanto o código dentro de um módulo está relacionado. Quanto maior, melhor.
Acoplamento indica o quanto os módulos dependem uns dos outros. Quanto menor, melhor.
Existe uma forma simples de avaliar.
| Pergunta | Sinal |
|---|---|
| É preciso alterar vários módulos ao mesmo tempo para corrigir uma funcionalidade? | Acoplamento alto |
| Há código sem relação misturado dentro do módulo? | Coesão baixa |
| Ao excluir um módulo, desaparece exatamente aquela funcionalidade? | Está bem dividido |
O critério principal é a mudança. O código que muda junto deve ficar em um módulo; o que muda por outros motivos, em outro.
É um princípio conhecido, certo? A «razão da mudança» do princípio da responsabilidade única (SRP) de SOLID foi ampliada para o nível dos módulos.
O que melhora com a modularização?
O custo de mudança é o primeiro a diminuir.
Com limites definidos, o impacto de uma alteração fica confinado ao módulo. O revisor pode concluir que o exterior está seguro se a interface pública do módulo não mudou.
A divisão do trabalho entre times também fica mais fácil. Separar responsáveis por módulo reduz conflitos por invadir a área de trabalho uns dos outros.
Os testes também ficam mais leves. É possível testar um único módulo isoladamente, sem iniciar o aplicativo inteiro.
A velocidade do build também melhora, pois basta recompilar o módulo alterado. Mas isso é consequência, não objetivo. Se os limites forem mal projetados, tudo precisará ser recompilado sempre.
Em Swift, os limites aparecem por meio dos modificadores de acesso.
// Apenas protocolos são expostos fora do módulo
public protocol PriceFormatter {
func format(_ amount: Int) -> String
}
// A implementação fica oculta dentro do módulo
final class KoreanPriceFormatter: PriceFormatter {
func format(_ amount: Int) -> String { "\(amount)ou" }
}
De fora, basta conhecer PriceFormatter. O ideal é que, mesmo trocando a implementação interna, o código externo ao módulo nem precise ser recompilado.
Quando dividir e quando esperar?
A modularização não é gratuita. Criar limites traz custos de design de interfaces, gerenciamento de versões e configuração do projeto.
| Situação | Critério |
|---|---|
| Uma alteração modifica código de vários times ou áreas | É hora de dividir |
| O build lento interrompe o fluxo de desenvolvimento com frequência | Divida, mas projete primeiro os limites de mudança |
| Produto inicial cujos limites de domínio ainda mudam com frequência | Espere até os limites se estabilizarem |
| Aplicativo pequeno feito por uma única pessoa | Organização por pastas e modificadores de acesso são suficientes |
Modularizar com limites errados é pior do que não modularizar. Se dois módulos sempre mudam juntos, o limite está errado. O certo é uni-los.
É assim que perguntam em entrevistas
P. Explique coesão e acoplamento e descreva a relação entre eles.
Coesão é a relação entre os elementos internos de um módulo; acoplamento é o grau de dependência entre módulos. Um bom design busca alta coesão e baixo acoplamento: ao reunir o código relacionado, naturalmente diminui o que é trocado com o exterior, reduzindo o acoplamento. É uma relação complementar.
P. Qual critério você usaria para dividir módulos?
Eu usaria a razão da mudança. O código que muda junto pelo mesmo motivo deve ficar em um módulo, enquanto o que muda por motivos diferentes deve ser separado. O limite é correto quando é possível responder qual decisão de design o módulo oculta, e não apenas listar suas funcionalidades.
No próximo artigo, trataremos dos problemas inevitáveis depois de dividir módulos: a direção das dependências entre eles e as dependências circulares.
Projetar as relações depois da divisão é a parte realmente difícil. Com a noção de coesão e acoplamento deste artigo, tudo ficará muito mais fácil.

![Imagem de capa de [Modularização #1] O que é modularização? (coesão e acoplamento)](/assets/images/posts/5d4a5830-3a61-4147-8b36-54b584c1f2ab/1.jpg)