📄
概念 📚 software-design-concepts

モジュール分割と依存管理

高凝集・低結合の原則に基づくモジュール設計と、循環依存・依存性逆転の実践

モジュール分割とは、コードベースを意味のある単位に区切り、各モジュールの責務を明確にする設計活動である。良いモジュール設計の目標は「高凝集・低結合(High Cohesion, Low Coupling)」であり、モジュール内部の要素は強く関連し合い、モジュール間の依存は最小限に抑える。この原則に従うことで、変更の影響範囲を局所化し、テスタビリティとメンテナンス性を高めることができる。

依存性逆転原則(DIP: Dependency Inversion Principle)は、上位モジュールが下位モジュールの具象に依存するのではなく、両者が抽象(インターフェースや抽象クラス)に依存するべきという原則である。依存性注入(DI: Dependency Injection)はこれを実現する代表的な手法で、具象の生成と利用を分離することでモジュール間の結合を抽象レベルに留める。これによりテスト時にモックを注入できるため、単体テストの容易性が大幅に向上する。

モノレポ(monorepo)では複数パッケージが同一リポジトリに共存するため、パッケージ境界の設計が特に重要になる。ある機能が複数パッケージにまたがって変更を引き起こすなら、パッケージの凝集度が不足しているサインである。また Barrel imports(index.ts でまとめて再エクスポートするパターン)は利便性が高い反面、意図しないモジュールへの依存を隠蔽し、バンドルサイズの肥大化やツリーシェイキングの妨げになる場合がある。

コードレビューで着目するポイント

  • import の方向が設計した依存グラフと一致しているか(下位モジュールが上位モジュールを参照していないか)
  • 循環依存(A → B → A)が発生していないか(ESLint の import/no-cycle や madge 等で検出可能)
  • インターフェースや抽象クラスを介した依存か、具象クラスへの直接依存か
  • DI コンテナや手動 DI の構成が適切で、テスト時に差し替え可能な設計になっているか
  • Barrel imports が意図しない依存の隠蔽やバンドルサイズの問題を引き起こしていないか
  • モノレポ内のパッケージ境界がビジネス上の境界(Bounded Context)と対応しているか
  • 共通ユーティリティが肥大化して「何でも屋モジュール」になっていないか

典型的なアンチパターン

循環依存(Circular Dependency): モジュール A がモジュール B をインポートし、B が A をインポートする関係。どちらを先にロードすべきか確定できないため、ランタイムエラーや予期しない初期化順序の問題を引き起こす。依存グラフを常に有向非巡回グラフ(DAG)に保つことが原則。

God Module: 本来複数のモジュールに分散すべき責務を一つのファイルやパッケージに集中させた設計。変更の影響範囲が予測不能になり、テストの粒度も粗くなる。

過剰な Barrel exports: index.ts に全モジュールを再エクスポートすることで、実際には利用していない大量のコードが import の連鎖によってバンドルに含まれる問題。アプリケーションの初期ロード時間に悪影響を与える。

参考リソース