How would you architect two source Flows from ViewModel to data layer?

This tests merging parallel async streams into one reactive state. A strong answer uses combine on two cold repository Flows, exposes one UI state Flow from the ViewModel, and maps failures to error states. Red flag: using two coroutines mutating shared state.
WHAT THIS TESTS: This question probes your ability to model parallel asynchronous work as a declarative stream and to separate concerns across the UI, presentation, and data layers. The interviewer cares about lifecycle safety, backpressure, error boundaries, and whether you treat the ViewModel as a state producer rather than a callback aggregator.
A GOOD ANSWER COVERS: A good answer hits four things in order. First, the data layer exposes two independent cold Flows, typically returned from repository methods, each running its own network call on a dispatcher like IO. Second, the ViewModel consumes these Flows using combine, which waits for both to emit and pairs their results into a single UI state object. Third, that UI state is a sealed class or data class with fields for loading, data, and error, so the screen always receives a consistent snapshot rather than partial updates. Fourth, errors are caught using the catch operator on each source Flow before they reach combine, mapping Throwables to typed error states instead of letting them cancel the entire coroutine.
COMMON WRONG ANSWERS: Red flags include launching two separate coroutines in viewModelScope that each call suspend functions and then post to a shared MutableStateFlow. This pattern loses atomicity, invites race conditions, and makes loading state nearly impossible to track correctly. Another red flag is using a single try-catch block around both calls sequentially, which doubles latency by forcing the sources to run serially. Exposing Channel or hot SharedFlow from the repository without an explicit consumer strategy is also a mistake because it breaks the cold-stream contract and leaks resources.
LIKELY FOLLOW-UPS: The interviewer may ask how you would retry one failed source without reloading the other. They may also ask what happens if the ViewModel is cleared mid-flight, how you would handle a refresh trigger that restarts both streams, or whether you would cache the result in a Room database and expose it via Flow to survive process death.
ONE CONCRETE EXAMPLE: Imagine a dashboard that needs user profile data from endpoint A and transaction history from endpoint B. The ProfileRepository exposes a cold Flow of Profile objects and the TransactionRepository exposes a cold Flow of Transaction lists. In the ViewModel, you combine the two Flows so that each emission pairs the latest profile with the latest transactions into a single success state. You attach a catch operator before stateIn so that any network failure maps to an error state rather than cancelling the scope. You expose this through stateIn using viewModelScope, a five second WhileSubscribed timeout, and an initial loading state. The UI collects this single state Flow and renders the full snapshot. If one repository throws, the catch emits an error state and the other source continues unaffected.
Read the original → developer.android.com
- #android
- #kotlin-coroutines
- #flow
- #viewmodel
- #app-architecture
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.