概要
JavaScriptにおける“モジュール”とは、
機能・責務ごとに分割された再利用可能なコードの単位であり、アプリケーションの複雑性に対抗する最小構成要素である。
本稿では、構造としてのモジュール設計、依存の分離、インターフェースの設計、抽象化の階層化について、以下の観点から整理する:
- ESModulesとCommonJSの根本的な違い
- モジュール境界の定義と責任分離
- 依存性の逆転と抽象化設計
- インポート設計とネームスペース戦略
- プロジェクト構造におけるモジュール戦略
1. モジュールとは何か?
- ✅ 明示的な**入出力(export/import)**を持つ
- ✅ ひとつの責務に閉じた機能を定義する
- ✅ 再利用されることを前提とする
2. ESModules vs CommonJS
特徴 | ESModules | CommonJS |
---|---|---|
シンタックス |
import / export
|
require / module.exports
|
解決タイミング | 静的(ビルド時) | 動的(実行時) |
並行ロード | 可能(ブラウザ対応) | 不可 |
使用場面 | フロントエンド、ES環境全般 | Node.jsでの従来コード |
→ ✅ モダンな開発ではESModulesが標準。ツリーシェイキングや型支援との親和性も高い。
3. モジュール境界と責務分離
✅ モジュールは1機能=1責任が原則
// utils/formatDate.js
export function formatDate(date, format = 'YYYY-MM-DD') { ... }
// services/userService.js
export async function fetchUser(id) { ... }
→ ✅ ユーティリティ、ドメインロジック、UIなどで役割単位に分割
4. 依存性の設計:注入と抽象化
❌ モジュール内部で依存を直接呼び出す
// services/userService.js
import { getToken } from '../auth';
export function fetchUser(id) {
const token = getToken(); // tightly coupled
}
✅ 依存を外部から渡す(依存性注入)
// services/userService.js
export function createUserService(tokenProvider) {
return {
fetchUser(id) {
const token = tokenProvider();
...
}
};
}
→ ✅ モックやテスト時に入れ替え可能
→ ✅ 実装に依存せず、抽象に依存する
5. モジュールインターフェース設計の原則
✅ インターフェースは最低限の公開
// api/userApi.js
export async function getUser() { ... }
// export const API_KEY = '...'; ← ❌ 内部定数を漏らさない
- ✅
default export
は基本避け、名前付きエクスポートを推奨 - ✅ グルーピングが必要ならオブジェクト構造にまとめる
6. モジュール連携と依存方向の設計
- 上位層(UI, Command) → 下位層(Domain, Service)
- “データが流れる方向”は常に一方向に
- ✅ 双方向依存を避ける
[UI] ─→ [Service] ─→ [Repository]
↑
[Utilities]
→ ✅ 高レベルモジュールは、抽象に依存することで結合度を下げる
7. プロジェクト構造とモジュール整理
/src
/modules
/user
userService.js
userValidator.js
userTypes.js
/product
productService.js
/shared
utils/
constants/
hooks/
- ✅ ドメイン単位でディレクトリを分離
- ✅ 共有ロジックは
shared
以下に集約
設計判断フロー
① このコードは単独で再利用される? → モジュール化候補
② 外部依存を直接呼んでる? → 注入可能な構造へ
③ 公開範囲は最小限? → exportを明示的に設計
④ モジュール間の依存方向は一方通行? → サイクル構造を解消
⑤ UI・ドメイン・ユーティリティが混在してない? → 層を整理
よくあるミスと対策
❌ ディレクトリ単位で責務が曖昧
→ ✅ “どの機能のためのコードか”で階層を決める
❌ default exportを乱用し、名前が不透明
→ ✅ 明示的な命名付きエクスポートで可読性を担保
❌ 1ファイルに複数の責務が混在
→ ✅ 各ファイルは単一の目的に特化すべき
結語
モジュール設計とは、**“変更に強く、使い回しが効き、意図が明確な構造を作ること”**である。
- 責任の分離
- 依存の注入
- 抽象との接続
- 明示的な入出力
- 結合を避けた設計
設計されたモジュールは、コードの粒度を洗練し、アプリケーションの成長に耐える基盤となる。