DEV Community

Eduardo Rabelo
Eduardo Rabelo

Posted on

JavaScript: Eliminando Dependências Circulares

Dependências circulares (também conhecidas como dependências cíclicas) ocorrem quando dois ou mais módulos fazem referência um ao outro.

Esta poderia ser uma referência direta (A -> B -> A):

// arquivo a.ts
import { b } from 'b';
...
export a;

// arquivo b.ts
import { a } from 'a';
...
export b;

ou indireta ( A -> B -> C -> A):

// arquivo a.ts
import { b } from 'b';
...
export a;

// arquivo b.ts
import { c } from 'c';
...
export b;

// arquivo c.ts
import { a } from 'a';
...
export c;

Embora as dependências circulares não resultam diretamente em erros (elas certamente podem), quase sempre terão conseqüências não intencionais. Em nosso projeto, estávamos experimentando lentidão na verificação de tipos do TypeScript e freqüentes falhas de "falta de memória" do nosso servidor JavaScript.

O Node.js suporta instruções circulares require/import entre os módulos, mas pode ficar confuso rapidamente. Na documentação do Node.js, diz: "É necessário um planejamento cuidadoso para permitir que as dependências do módulo cíclico funcionem corretamente em um aplicativo".

Na minha experiência, a melhor maneira de lidar com dependências circulares é evitá-las completamente. Dependências circulares são geralmente uma indicação de design de código incorreto e devem ser refatoradas e removidas, se possível.

Verificando Dependências Circulares

Embora existam alguns pacotes de Node que executam a análise estática para procurar por dependências circulares, percebi que eles não funcionam muito bem. Alguns dos pacotes encontraram algumas dependências circulares, enquanto outros erraram completamente todas elas. O melhor verificador de dependência circular que encontrei funciona na camada de empacotamento. O plugin circular-dependency-plugin do webpack foi bastante eficiente e muito simples de usar.

Pegando o exemplo da documentação do circular-dependency-plugin:

// webpack.config.js
const CircularDependencyPlugin = require('circular-dependency-plugin')

module.exports = {
  entry: "./src/index",
  plugins: [
    new CircularDependencyPlugin({
      // exclude detection of files based on a RegExp
      exclude: /a\.js|node_modules/,
      // add errors to webpack instead of warnings
      failOnError: true,
      // allow import cycles that include an asyncronous import,
      // e.g. via import(/* webpackMode: "weak" */ './file.js')
      allowAsyncCycles: false,
      // set the current working directory for displaying module paths
      cwd: process.cwd(),
    })
  ]
}

Imediatamente, o plugin encontrou todos os tipos de dependências circulares que foram introduzidas durante o projeto:

Saída do `circular-dependency-plugin`

Corrigindo as dependências circulares

Existem algumas opções para se livrar das dependências circulares. Para uma cadeia mais longa A -> B -> C -> D -> A, se uma das referências for removida (por exemplo, a referência D -> A), o padrão de referência cíclica também será quebrado.

Para padrões mais simples, como A -> B -> A, uma refatoração pode ser necessário. Talvez os módulos que vivem em B possam ser movidos para A. Ou, o código necessário poderia ser extraído para um C e tanto A e B podem fazer referência. Se os dois módulos executam comportamentos semelhantes, eles também podem ser combinados em um único módulo.

Consertar um grande número de dependências circulares pode ser um compromisso significativo, mas melhora a capacidade de manutenção da base de código e pode reduzir erros no futuro. Ao deixar o plug-in de dependência circular no pipeline do webpack, ele pode ser executado com freqüência e as dependências circulares serão encontradas imediatamente após serem introduzidas.

Da próxima vez que eu estiver iniciando um projeto e configurando as opções do webpack, incluirei esse plug-in no primeiro dia!

Créditos ⭐️

Top comments (6)

Collapse
 
rafaelassumpcao profile image
Rafael A

opa edu, parabens pelo artigo está excelente.

Em casos em que eu não tenho o webpack para fazer uso, você indica algum outro verificador de dependecias circulares que perfoma tão bem quanto este plugin?

valeu!

Collapse
 
oieduardorabelo profile image
Eduardo Rabelo • Edited

fala rafa! geralmente você precisa instalar alguma outra ferramenta,

em projetos que não usam webpack, eu geralmente tenho uma branch na minha máquina e instalo o webpack+circular-dependency-plugin, aponto para o arquivo principal (por exemplo, um servidor em Node.js, entry: "./server.js"), e rodo a análise!

eu não faço o commit do webpack, deixo tudo local e vou melhorando o projeto caso algum erro apareça!

já vi projetos com npmjs.com/package/madge, onde você pode fazer: madge -c ./server.js (ou qualquer outro arquivo que você queria), e até onde testei, os outputs são os mesmo e bem eficientes!

👋

Collapse
 
rafaelassumpcao profile image
Rafael A

puxa, muito boa a ideia obrigado mesmo Edu pela dica.

Collapse
 
demenezes profile image
deMenezes

A primeira vez que enfrentei isso foi com um projeto em ReactJS e NextJS, a ferramenta que me avisou foi o ESLint

Collapse
 
lucasruy profile image
Lucas Ruy

Muito interessante o seu artigo, obrigado por compartilhar esse conhecimento com a comunidade!

Collapse
 
jardelbordignon profile image
Jardel Bordignon • Edited

Muito interessante o assunto, obrigado por compartilhar, vou ficar atento a isso.