導入
TaskRepositoryはTaskItemをそのまま出し入れするだけで、中身の状態(完了かどうか、タイトルが重複していないか)には関心がありません。「未完了だけを見せる」「同じタイトルは禁止する」といった判断は、Repositoryではなく別の層が持つべきです。この判断を担当するのがTaskServiceです。
説明
flowchart LR
VM["ViewModel(画面の状態)"] --> SVC["TaskService<br/>業務ルール・判断"]
SVC --> REPO["TaskRepository<br/>データの出し入れだけ"]
REPO -.-> MEM["InMemoryTaskRepository"]
style SVC fill:#e1f5fe
役割の違いをコードで見ます。
// Repository: 「そのまま出し入れ」しかしない(判断なし)
List<TaskItem> all = repository.findAll();
// Service: 「未完了だけ」という判断(業務ルール)を持つ
int remaining = service.remainingCount();
TaskRepositoryはfindAll()で全件をそのまま返すだけで、完了かどうかを気にしないTaskServiceはTaskRepositoryを使いながら、「未完了は何件か」「重複タイトルは禁止」のような判断を追加する- 画面側(ViewModel)はServiceだけを呼べばよく、Repositoryの存在すら知らなくてよい
業務ルールとEntityの検証の違い
TaskTitleはコンストラクタで「空文字は禁止」「50文字を超えたら禁止」を検証します。これは1つのタイトル単体で完結する検証なので、Entity(値オブジェクト)の責任です。一方「既存のタスクと同じタイトルは登録できない」というルールは、他のタスクと見比べる必要があります。1つのデータだけでは判断できず、複数のデータを横断する判断だからこそ、Repositoryを持つServiceの仕事になります。
まとめ
- Repositoryは「そのまま出し入れ」、Serviceは「業務ルール・判断」を担当する
- 1つのデータで完結する検証はEntity、複数のデータを見比べる判断はServiceが持つ
- ViewModelはServiceだけを呼び、Repositoryの細部を知らなくてよい
次回: TaskServiceにルールを足しながら、TDDで実装していきます。