Stop Circular Dependencies With SDP

循環参照はサイレントキラーです。ビルドエラーを引き起こすことはありません。インポート時にランタイム例外を発生させることもありません。その代わりに、数週間後に本番環境で現れる、微妙な undefined 値を引き起こします。

モジュールAがBからインポートし、BがAからインポートし返すときにサイクルが発生します。

JavaScriptはこれらのインポートを静かに解決します。モジュールBが要求したときにモジュールAがまだロード中である場合、JavaScriptは空のオブジェクトを返します。その結果、コードは後になって失敗し、スタックトレースは間違った場所を指し示します。

これは dependency-cruiser で解決できます。このツールはファイルをスキャンし、依存関係グラフをマッピングします。TypeScriptやモノレポにも対応しています。

セットアップ方法:

  • devDependenciesに追加します: yarn add -D dependency-cruiser
  • package.json にスクリプトを追加します: "depcruise": "depcruise packages --config .dependency-cruiser.js"
  • サイクルを検知するための .dependency-cruiser.js ファイルを作成します。

しかし、サイクルを見つけるのは戦いの半分に過ぎません。サイクルが形成されるのを防がなければなりません。

Stable Dependencies Principle (SDP) を活用しましょう。このルールは、安定性の方向に向かって依存しなければならないと定めています。

安定性は構造的な特性です。多くの他のモジュールがそのモジュールに依存しているとき、そのモジュールは安定しています。安定したモジュールを変更すると、多くの人に影響を与えます。このコストが、そのモジュールを安定させているのです。

不安定性(Instability)の公式を使用します: I = Fan-Out / (Fan-In + Fan-Out)

• I = 0 は、モジュールが最大限に安定していることを意味します。すべてがそのモジュールに依存しており、そのモジュールは何にも依存していません。 • I = 1 は、モジュールが最大限に不安定であることを意味します。それは多くのものに依存しており、何もそのモジュールに依存していません。

ルール:あるモジュールの不安定性スコアは、それがインポートするすべてのモジュールのスコアよりも高くなければなりません。依存の矢印は、より低い不安定性の方向に向いている必要があります。

低レベルの utils (I ≈ 0) は、高レベルの pages (I ≈ 1) からインポートしてはいけません。

この原則を dependency-cruiser を使って自動化されたルールに変換できます。単にサイクルをチェックするのではなく、方向性をチェックします:

  • utilsfeatures からインポートするのを防ぐ。
  • featurespages からインポートするのを防ぐ。

これにより、サイクルを生み出す構造的な条件を阻止できます。バグに対処するのではなく、バグを未然に防ぐことができるようになります。

Source: https://dev.to/wojciech_kot_b82f5d7cbfc6/stop-circular-dependencies-before-they-stop-you-dependency-cruiser-the-stable-dependencies-34ho