Data SourceでFlowを生成し、Repositoryで加工・統合し、ViewModelでUI Stateに変換する。

🧑🏻💻 データソースに応じて Flow の扱いを決める
データソースによって、データの取得方法は異なります。

Room / DataStore :
・ すでに Flow を提供しているため、新たに Flow でラップする必要はありません。
・ データの変更を監視する仕組みもデータソース側に備わっています。
Callback / Listener :
・ callbackFlow を使って Flow に変換します。
・ awaitClose で購読終了時に Listener を解除します。
・ Callback の登録・解除を管理するデータソースの近くで変換するのが適切です。
suspend 関数 :
・ 一度だけ結果を取得する処理なら、無理に Flow へ変換する必要はありません。
・ Flow に変換すると、購読するたびに API が実行されます。
・ 複数の suspend 処理をまとめてストリームとして扱いたい場合などに Flow を利用します。
🧑🏻💻 Flow への変換は Data Source で行う
Callback API を Flow に変換する場合、Repository ではなく Data Source で行います。
推奨する構成
Data Source
|
| Callback
v
callbackFlow
|
v
Flow
|
v
Repository
・ 基盤 API とのやり取りを担当する Data Source に属する責務です。
・ Data Source は API との接続や Listener のライフサイクルを管理します。
・ Repository は Flow を受け取り、必要に応じて加工・統合します。
🧑🏻💻 Repository では Flow を加工・統合する
Data Source から Flow を受け取った Repository では、データをアプリケーションで扱いやすい形に変換します。
主な処理は次の3つです。

・ ここで重要なのは、Repository は新しいデータソースを作る場所ではなく、既存のデータストリームをアプリケーションに適した形へ変換する場所ということです。
🧑🏻💻 ViewModel では FlowをStateFlow に変換する
Repository から受け取った Flow は、ViewModel で UI State に変換します。
ここで利用するのが stateIn() です。
val uiState: StateFlow<UiState> = combine(
repository.items,
query,
repository.showAds
) { items, query, showAds ->
UiState(
items = items.filter { it.matches(query) },
showAds = showAds
)
}.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000),
UiState()
)
・ combine によって、Repository のデータやユーザー入力に変更があれば、新しい UI State が導出されます。
・ UI State を手動で更新する必要はありません。
・ stateIn() によって、現在の状態を保持する StateFlow に変換します。
ここでの Flow 変換は、Data Source で行う Flow 変換とは目的が異なります。

🧑🏻💻 全体のアーキテクチャ
Data Sources
|
+-----------+-----------+
| | |
Room Callback suspend API
| | |
Flow callbackFlow suspend
| | |
+-----------+-----------+
|
v
Repository
|
transform / combine
|
Flow<T>
|
v
ViewModel
|
map / combine
|
stateIn()
|
StateFlow<UiState>
|
v
UI
|
collectAsStateWithLifecycle()
・ suspend API は必ずしも Flow に変換する必要はありません。
・ 必要に応じて Repository や ViewModel から suspend 関数を呼び出せます。
・ この図は監視対象となるデータの一般的な流れであり、すべての処理に Flow を要求するものではありません。
🧑🏻💻 まとめ
最も重要なのは、Flow をどこでも生成できるからといって、どこでも生成すべきではないということです。
Data Source : Flow を生成する
Repository : Flow を加工・統合する
ViewModel : StateFlow として UI State を導出する
UI : StateFlow を購読する
Convert at the source. Transform in the Repository. Derive UI state in the ViewModel.
つまり、Flow を導入するタイミングは、データの取得方法と各レイヤーの責務によって決めるべきです。
Related Categories : Android・Developmemt・JetpackCompose・KMP・Kotlin・Kotlin Multiplatform Mobile・Newbie・Recommended