概要
「ユーティリティ関数(utility)」は、よく使う処理を再利用可能にまとめるという発想に基づくが、
無差別に再利用可能にすることが目的ではない。
それは**“汎用性と責務を適切にバランスさせ、ドメインロジックと分離することでアプリケーションの一貫性を支える構造”**である。
本稿では、ユーティリティ関数の設計原則、ドメイン層との分離基準、命名・引数設計・テスト容易性の観点からの最適化戦略を解説する。
1. ユーティリティ関数とは
- ✅ 汎用性が高く、アプリケーション全体で再利用可能
- ✅ 状態を持たず、純粋関数であることが原則
- ✅ ドメイン(業務)知識に依存しない、技術的/構文的処理
export function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
2. ドメイン関数との分離
// ユーティリティ関数(純粋・再利用可)
export function padZero(num, length = 2) {
return num.toString().padStart(length, '0');
}
// ドメイン関数(ビジネスルールあり)
export function formatInvoiceId(id) {
return `INV-${padZero(id)}`;
}
- ✅ 汎用関数はドメイン知識から隔離
- ✅ ビジネスルールはアプリ層で閉じる
3. 汎用性の設計判断
判定基準 | 内容 |
---|---|
入力と出力が単純か? | 引数・戻り値が明確で、副作用がないか |
状態や依存に左右されないか? | 内部で外部関数やグローバル参照がないか |
ビジネス文脈に依存していないか? | アプリケーションの文脈外でも使えるか |
4. 命名規則と責務の粒度設計
// ✅ 良い命名
parseDate() // パースする
formatCurrency() // 整形する
toSlug() // 変換する
// ❌ 曖昧な命名
handleStuff()
processThing()
- ✅ 動詞 + オブジェクト形式で意図を明確に
- ✅ 責務は1つに →
isEmpty()
とisBlank()
を同居させない
5. テスト容易性と構造
// utils/date.ts
export function getWeekDay(date) {
return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][date.getDay()];
}
test('getWeekDay returns correct abbreviation', () => {
expect(getWeekDay(new Date('2023-05-14'))).toBe('Sun');
});
- ✅ グローバル状態に依存せず、完全にピュアな関数であること
- ✅ 構文・ユースケースの境界に立つよう設計
設計判断フロー
① 関数はドメイン知識に依存していないか? → ユーティリティへ分離可
② 状態や副作用は含まれていないか? → 純粋関数に限定
③ 再利用性と読みやすさのバランスは取れているか? → 命名で意図明示
④ 他のモジュールへの依存があるか? → 依存を排除または注入に切り替える
⑤ 処理の粒度は細かすぎず、適切か? → 役割ごとに関数を分割 or 集約
よくあるミスと対策
❌ utils.js
に全てを詰め込んで1000行以上の肥大化
→ ✅ 機能単位でモジュールを分割(utils/date.ts, utils/math.tsなど)
❌ 関数内でDOMやlocalStorageを操作しており純粋性を失っている
→ ✅ 副作用は外部で処理し、ユーティリティ関数は値変換に徹する
❌ ドメインロジックを抽象化しすぎて意味が不明瞭に
→ ✅ 「誰が使うのか」「何をするのか」が明確な名前と構造に修正
結語
ユーティリティ関数とは、「どこでも使える便利な関数」ではない。
それは**“汎用性と責務の分離を通して、再利用性と設計一貫性を担保する構造要素”**である。
- ドメイン知識から分離し、純粋性と独立性を保つ
- 汎用性の名のもとに責務を曖昧にしない
- テスト容易性と意図の明示が、設計としての完成度を高める
JavaScriptにおけるユーティリティ設計とは、
“再利用可能な構文を責任ある形で抽出し、設計の秩序と整合性を維持するための戦略である。”