Domain と UseCase の違いを整理してみる
Clean Architecture や DDD を学んでいると、ほぼ確実に次の疑問にぶつかります。
- Domain と UseCase の違いがよく分からない
- どこまでが Domain で、どこからが UseCase なのか曖昧
- UseCase を作ろうとすると、何を書けばいいのか分からない
私自身、このあたりで何度も立ち止まりました。 この記事では、実装経験を通して整理できた Domain と UseCase の違い を、自分なりの言葉でまとめてみます。
一言で言うと何が違うのか
まず、かなり大胆に要約します。
Domain →「この世界では、何が正しく、何が成り立つか」を表す
UseCase →「その正しさを、どういう手順・文脈で使うか」を表す
この一文を起点にすると、境界が見えやすくなります。
Domain とは何か
Domain は「意味」と「ルール」の集合体
Domain が表すのは、次のようなものです。
- アプリが扱う概念(名詞)
- その概念に関する制約
- 不変条件
- 正しい / 正しくないの判断基準
重要なのは、Domain が 技術から切り離されている という点です。
Domain の例
@JvmInline
value class SourceId(val value: String)
data class Source(
val id: SourceId,
val name: String,
val isActive: Boolean
) {
init {
require(name.isNotBlank())
}
}
ここでやっているのは、
- 「Source とは何か」の定義
- 名前は空ではいけない、というルール
であって、
- DB
- API
- Flow
- Android
といったものは一切登場しません。
Domain は 世界観そのもの を表します。
Domain がやらないこと
これはかなり重要です。
Domain は、次のようなことをやりません。
- データを取得する
- 保存する
- 非同期処理を扱う
- 時系列を持つ
- Repository を呼ぶ
Domain は常に 現在形 で存在します。
UseCase とは何か
UseCase は「動詞の層」
UseCase は、Domain とは役割がはっきり違います。
UseCase が表すのは、
- ユーザーの行動
- アプリとしての操作単位
- 一連の手順や流れ
つまり、何かをする という文脈です。
UseCase の例
class ObserveSourcesUseCase(
private val repo: SourceRepository
) {
operator fun invoke(): Flow<List<Source>> =
repo.observeSources()
.map { list ->
list.filter { it.isActive }
}
}
このコードには、
- Repository
- Flow
- データの流れ
- 条件による選別
といった 時間や手順 の概念が含まれています。
これが Domain との決定的な違いです。
Domain と UseCase の関係
関係性を整理すると、次のようになります。
- Domain は単体で完結する
- UseCase は Domain を使って何かをする
- Domain は「正しさ」を持つ
- UseCase は「使われ方」を持つ
UseCase は Domain の上位互換ではありません。 役割がまったく違う層 です。
なぜ UseCase が分かりにくいのか
UseCase が分かりにくくなる原因は、だいたい次のどれかです。
- Repository の薄いラッパーになっている
- CRUD をそのまま移しただけ
- 「層を作ること」が目的になっている
こうなると、
「これ、本当に必要?」
という感覚になります。
その違和感は正しいです。
UseCase を作るべきタイミング
私の中での判断基準は、次のようなケースです。
- 複数の Repository をまたぐ
- データを加工して意味のある形にする
- List / Map / Filter / Sort などを組み合わせる
- UI から切り離したい判断が含まれる
これらは 「どう使うか」 の話なので、UseCase の責務になります。
Entity → Domain 変換はどこでやるべきか
よくある疑問に、
Entity を Domain に変換するのは UseCase? Repository?
というものがあります。
答えは一択ではありません。
- Entity と Domain がほぼ 1:1 → Repository で変換するのが自然
- ユースケースごとに意味が変わる → UseCase で変換するのが自然
重要なのは、ViewModel が Entity を知らないこと です。
Domain と UseCase を分ける最大のポイント
自分なりに一番しっくり来た整理は、これです。
Domain は「これは正しいか」を決める UseCase は「それをいつ・どう使うか」を決める
この2つを混ぜ始めると、境界は一気に崩れます。
よくあるアンチパターン
- Domain に Flow が出てくる → 時間の概念が混ざっている
- UseCase に Entity が出てくる → インフラが漏れている
- UseCase が ViewModel の代わりになっている → UI 依存が侵食している
このあたりは要注意です。
おわりに
Domain と UseCase の違いは、 ルールを暗記しても分かりにくい部分です。
実装しながら、
- これは「意味」なのか
- これは「使い方」なのか
と問い続けることで、少しずつ輪郭が見えてきました。