UIイベントをViewModel経由にしても責務は増えるだけ。Composeの副作用で完結させたほうがシンプルになる。

🧑🏻💻 「AnalyticsはViewModelから送るべき」という思い込み
Jetpack Composeでは、ボタンタップや画面表示などのイベントをGoogle Analyticsへ送る機会が多くあります。
しかし、多くのMVVMサンプルでは次のようなコードになっています。
Button
│
▼
ViewModel.onClick()
│
▼
Analytics.log(...)
一見MVVMらしく見えます。
ですが、本当にViewModelが必要なのでしょうか。
Composeでは、多くの場合答えは No です。
🧑🏻💻 AnalyticsはUIイベントである
Google Analyticsが知りたいのは
- ボタンが押された。
- 画面が表示された。
- ダイアログが開いた。
- スクロールした。
- タブが切り替わった。
つまり
「UIで何が起きたか」
です。
これはRepositoryのデータでもなく、
Business Logicでもありません。
純粋なUIイベントです。
🧑🏻💻 ViewModelは何も判断していない
例えば
fun onFavoriteClick() {
analytics.log("favorite")
}
これだけなら
ViewModelは
- 状態も持たない。
- ロジックもない。
- ただ転送しているだけ。
です。
実質
Button
│
▼
Analytics
との違いがありません。
🧑🏻💻 Composeならその場で書ける
Button(
onClick = {
analytics.logEvent("favorite")
onFavorite()
}
) {
Text("Favorite")
}
これだけです。
余計な
- Event
- Action
- Intent
- ViewModel関数
は不要です。
🧑🏻💻 画面表示も同じ
よくある実装
LaunchedEffect(Unit) {
viewModel.onScreenShown()
}
ViewModel
fun onScreenShown() {
analytics.logScreen("Home")
}
ですが、
Composeなら
LaunchedEffect(Unit) {
analytics.logScreen("Home")
}
だけで終わります。
ViewModelは何もしていません。
🧑🏻💻 スクロールイベントも同じ
Composeでは
LaunchedEffect(listState) {
snapshotFlow {
listState.firstVisibleItemIndex
}.collect {
analytics.logScroll(it)
}
}
で十分です。
これを
snapshotFlow
│
▼
ViewModel
│
▼
Analytics
にする理由はありません。
🧑🏻💻 MVVMに乗せることで増えるもの
UI
│
▼
ViewModel
│
▼
Analytics
追加されるもの
- Eventクラス
- ViewModel関数
- DI
- テスト対象
- 呼び出し経路
しかし
増える責務はありません。
🧑🏻💻 ViewModelはUIの代理人ではない
ViewModelの役割は
- UI Stateを持つ。
- Business Logicを実行する。
- Repositoryと連携する。
ことです。
Analytics送信だけのために
fun onButtonClick()
fun onFabClick()
fun onBackPressed()
fun onTabSelected()
fun onDialogOpened()
を大量に並べると、
ViewModelは単なるイベント中継器になります。
🧑🏻💻 Composeには副作用APIがある
Composeには
- LaunchedEffect
- DisposableEffect
- SideEffect
- snapshotFlow
があります。
これらは
UIから外部世界へ何かを通知する
ために存在しています。
Analytics送信はまさに副作用です。
Compose自身が用意している仕組みを使う方が自然です。
🧑🏻💻 UIイベントはUIで完結させる
例えば
Button(
onClick = {
analytics.logEvent("purchase")
onPurchase()
}
)
これだけです。
ViewModelを経由する理由はありません。
🧑🏻💻 ではViewModelで送るべきケースは?
もちろんあります。
例えば
purchaseRepository.purchase()
analytics.logPurchase(
price,
currency
)
購入成功後に
- 金額
- 商品ID
- 購入結果
など
Business Logicの結果
を送る場合です。
この情報はUIでは分かりません。
このようなAnalyticsはViewModelやUseCaseから送るべきです。
🧑🏻💻 判断基準

🧑🏻💻 一つの基準
次の質問をすると判断しやすくなります。
このイベントはUIだけで完結しているか?
YESなら
Composeから直接送る。
NOなら
ViewModelやUseCaseから送る。
これだけです。
🧑🏻💻 まとめ
Composeでは、副作用を扱うためのAPIが最初から用意されています。
Google Analyticsのような「UIで何が起きたか」を記録する処理は、その場で副作用として送るほうがシンプルです。
一方で、ビジネスロジックの結果やRepositoryの処理結果に基づくAnalyticsは、ViewModelやUseCaseが適切な責務を持ちます。
MVVMは「すべての処理をViewModel経由にする」ための設計ではありません。
Analyticsを送る場所も、「誰がその情報を知っているか」で決めるべきです。
Related Categories : Android・JetpackCompose・Kotlin・Recommended