質問するログイン新規登録

Q&A

解決済

1回答

560閲覧

TypeScript/ドメインサービス設計: ファクトリ関数と関数群の使い分けについて

kuzuha

総合スコア6

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

0クリップ

投稿2025/12/05 01:12

0

0

質問

ドメイン層のサービス実装において、以下の 2 つのパターンのどちらを採用すべきか判断に迷っています。

現状の 2 つのパターン

パターン 1: 依存性注入が必要な場合(ファクトリ関数)

domain/ └── service.ts

typescript

1// domain/service.ts 2export function createServiceA(database: DatabaseFacade): ServiceA { 3 return { 4 async updateData(id: string, value: boolean): Promise<void> { 5 await database.update(id, value); 6 }, 7 async getData(): Promise<Data[]> { 8 return await database.getAll(); 9 }, 10 }; 11}

パターン 2: 依存性注入が不要な場合(関数群)

domain/ └── services/ └── processor.ts

typescript

1// domain/services/processor.ts 2export function processData(input: Input[]): Output[] { 3 return input.map((item) => ({ 4 id: item.id, 5 value: transform(item.value), 6 })); 7} 8 9function transform(value: string): string { 10 return value.toUpperCase(); 11} 12 13function validate(input: Input): boolean { 14 return input.id.length > 0; 15}

検討している 2 つの選択肢

選択肢 A: 全部ファクトリ関数で統一

domain/ └── service.ts // すべてのサービスをここに

typescript

1// 依存性あり 2createServiceA(database); 3 4// 依存性なし(空の引数でもOK) 5createProcessorService();

メリット:

  • 統一されたパターンで一貫性がある
  • 将来的に依存性が追加されても変更が少ない

デメリット:

  • 依存性がないのにファクトリ関数にするのは不自然かもしれない
  • 純粋関数をわざわざオブジェクトに包む必要がある

選択肢 B: 依存性の有無で区分

domain/ ├── service.ts // 依存性あり → ファクトリ関数 └── services/ └── processor.ts // 依存性なし → 関数群

typescript

1// 依存性あり → ファクトリ関数 2createServiceA(database); 3 4// 依存性なし → 関数群 5processData(input);

メリット:

  • 依存性の有無がコードから明確に分かる
  • 純粋関数はそのまま関数として定義できてシンプル
  • 不要な抽象化を避けられる

デメリット:

  • パターンが混在する
  • 将来的に依存性が追加された場合、関数群からファクトリ関数への変更が必要

質問

  1. どちらの選択肢がより良い設計だと思いますか?
  2. その判断理由を教えてください。
  3. 業界のベストプラクティスやモダンな実装パターンとして、どちらが推奨されますか?

補足情報

  • TypeScript と、コンポーネントという概念の入門者です。(JavaScript は書けますが、体系的な知識はありません。)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

guest

回答1

0

ベストアンサー

TypeScriptの型は構造(メンバ名の一致)で判断され、同名のメンバ名を持つオブジェクトは同一あるいは部分型とみなされます

class A{ F():number{ return 1 } } class B{ F():number{ return 2 } } const i:A=new B() console.log(i.F())
2

引数を必須とする関数型指向の設計とは本来反りが合いません
特にオブジェクトを引数に取る関数は可能な限り取り払い、オブジェクトの振る舞いとして提供される形が理想的です

厳格なルール規定とレビュー体制を両立するチーム開発であれば、依存性の明示は関数の実装意図を共有する意味で必要性を伴います

業務であればこれを達成することは前提要件として容易な一方、業務外では過去資産を含めライブラリの管理を煩雑にします

•メンバ名に被り(不要なオーバーライド)はないか
•引数にオブジェクトが正しく渡されているか

すなはち「関数に適用されるクラスの設計は適切か」を最低限意識し、不適切なオブジェクトを引き金とした動作不良に注意を払う必要があります
その労力と可読性のトレードオフをどの程度受容するかでそれぞれのモデルを選択すると良いでしょう

まとめると
•理想形としてAを推奨
•業務面ではBが推奨

となります

投稿2025/12/06 02:38

nanashi123

総合スコア171

nanashi123

2025/12/06 03:30

この選択は、その開発がどのレイヤーに属するかにもよっても決まります ライブラリの開発は原則として将来的な仕様変更を考慮しません これは都合によって仕様が変わるライブラリに信用がないからです 実装の改訂が互換性への直接的な破壊的変更を意味することはもとより、拡張と修正が同義となることで安全性に懸念が生じます 必要に迫られた場合は必要最小限度の追加に留めるか、新規にライブラリをサポートします 標準ライブラリに非推奨なクラスや関数が発生するのはこのためです 仕様変更を度外視するなら、そのライブラリに求められるのは分かりやすさであり、変更への耐性ではありません 業務面では特にこの色が強くなります 重要な点は多くの場合、ライブラリは作るものではなく使うものであることです 個人で作るプログラムでは主に将来的な拡張を視野に入れて設計します 安定性が確約できないプログラムで重視すべきはライブラリが担保する安全性にいかに依存するかであり、この前提では不要な引数を取りうるモデルは選択肢から除外されます
kuzuha

2025/12/07 05:56

ものすごく頭良いですね。難解ですが…明快でもあります。 丁寧なご回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問