2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScriptにおけるユースケース層設計戦略:責務の隔離、ロジックの集約、依存注入による柔軟性の確保

Posted at

概要

ユースケース層とは「関数をまとめる場所」ではない。
それは**“アプリケーションの振る舞いを、責務ごとに構造化し、依存から切り離す設計層”**である。

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におけるユースケース層設計とは、
“構造と拡張性を保証する、アプリケーション制御の心臓部”である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?