概要
ユースケース層とは「関数をまとめる場所」ではない。
それは**“アプリケーションの振る舞いを、責務ごとに構造化し、依存から切り離す設計層”**である。
JavaScript/TypeScriptでアプリケーションを設計する際、
UI・状態管理・非同期処理・API通信が混在してしまい、テスト性・再利用性・保守性が著しく損なわれるケースが多い。
ユースケース層を設計することで、振る舞いを意味単位に分離し、責任ある構造を組むことが可能になる。
1. ユースケース層の定義と責務
- UIをトリガーとして
- 状態・API・ドメインロジックを統合
- 成功・失敗を状態やUIに返す
export const loginUser = async (
input: LoginForm,
deps: { authApi: AuthApi; sessionStore: SessionStore }
): Promise<Result<void, LoginError>> => {
const res = await deps.authApi.login(input);
if (!res.ok) return { ok: false, error: res.error };
deps.sessionStore.set(res.data.session);
return { ok: true };
};
2. UIとの連携:Presentationからロジックを切り出す
// UI層 (React / Vue)
const onSubmit = async () => {
const result = await loginUser(form, { authApi, sessionStore });
if (!result.ok) showError(result.error.message);
};
- ✅ UIは「結果を受けて状態や画面を制御する」のみに集中
- ✅ ロジックは「何をどうするか」をユースケース層に集約
3. 依存注入(DI)による柔軟性の確保
type Deps = {
userApi: UserApi;
userStore: UserStore;
};
export const fetchUserDetail = (deps: Deps) => async (id: string) => {
const res = await deps.userApi.fetch(id);
if (res.ok) deps.userStore.set(res.data);
};
- ✅ 外部依存(API, Store, DB)は引数として明示的に注入
- ✅ テスト時にモック依存へ差し替え可能
4. ユースケースは「意味単位」で分離する
❌ userService.ts にすべての操作を詰め込む
✅ usecases/user/createUser.ts
usecases/user/deleteUser.ts
usecases/user/updateUser.ts
- ✅ 1ユースケース = 1ファイル / 1関数に分離
- ✅ ロジックの意図が明確になり、再利用可能になる
5. エラーハンドリングと結果構造の統一
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
- ✅ UIや呼び出し元がtry-catchに依存せず分岐処理可能
- ✅ 結果を構造的に返すことでUXと制御が明確になる
6. テスト性と非同期制御の分離
- ✅ 副作用は全て注入
- ✅
fetchUserDetail({ api: mockApi, store: memoryStore })('u01')
のようにテスト可能
設計判断フロー
① UIやAPIがロジックと密結合していないか? → ユースケースに分離
② 外部依存(API, DB, Store)が直接使われていないか? → DIで注入構造へ
③ ロジックがユースケース単位に意味を持っているか? → 1責務 = 1ユースケース
④ 結果構造が曖昧 or try-catch依存になっていないか? → Result構造で明示的に
⑤ テスト時にモック依存で差し替え可能か? → 引数注入設計がされているか
よくあるミスと対策
❌ UIコンポーネント内でfetchと状態更新をすべて実行
→ ✅ ユースケース関数で責務を分離し、UIは状態制御に集中
❌ APIの仕様変更で複数箇所が壊れる
→ ✅ APIとの直接依存をユースケースに集約し、他層と切り離す
❌ 非同期処理のテストが不可能
→ ✅ ユースケース関数に依存を注入する設計でモック可能に
結語
ユースケースとは「処理をまとめる場所」ではない。
それは**“アプリケーションの意味を分離し、責務を集中させ、構造を再現可能に保つ設計層”**である。
- 振る舞いをユースケースに集約し
- UIから依存とロジックを追い出し
- 成果と失敗を構造として返し
- 柔軟な依存注入でテスト性を担保する
JavaScript/TypeScriptにおけるユースケース層設計とは、
“構造と拡張性を保証する、アプリケーション制御の心臓部”である。