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's really being asked
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.
The full answer
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.
The mistakes people make
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.
What usually comes next
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.
A 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.
Interview question
Which approach correctly merges two cold repository Flows into a single UI state Flow while preserving atomic updates and isolating errors per source?
- a.Collect both Flows sequentially in a single coroutine with one try-catch block
- b.Convert both repository Flows to hot SharedFlows before collecting them
- c.Launch two coroutines that collect each Flow into a shared MutableStateFlow
- d.Use combine on both Flows, apply catch to each source, and expose via stateInCorrect
Why? this is the answer
combine pairs the latest emissions from both cold Flows into a single atomic UI state snapshot, and placing catch on each source before combine maps failures to typed error states without cancelling the entire coroutine. Launching two coroutines that mutate a shared MutableStateFlow loses atomicity and invites race conditions, while sequential collection doubles latency.
Just read this? Test yourself on what you have been reading.
Read the original → developer.android.com
- #android
- #kotlin-coroutines
- #flow
- #viewmodel
- #app-architecture
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on android — each one lists the topics its interview covers.
See open roles