LiveData Was a Breakthrough, but It's Now the Wrong Abstraction for Most of Your App
Teams that continue exposing LiveData from repositories lock themselves into a UI-centric data model that breaks in WorkManager jobs, background services, and tests. Moving to Flow at the data layer and reserving LiveData for thin UI observation removes an Android framework dependency from business logic and gives every consumer the freedom to collect, convert, or sample the same stream.
LiveData arrived when Android was drowning in callbacks and manual lifecycle checks. Its `observe(owner)` pattern automatically unsubscribed observers and prevented the crashes that came from updating destroyed activities. That single contribution made it indispensable for years. But LiveData models a current value, not a stream of values. It lacks native operators for debouncing, combining, or retrying, forcing developers into `MediatorLiveData` tangles and hand-rolled event wrappers like `SingleLiveEvent` when one-shot UI events are needed. Its threading model, where rapid `postValue` calls may drop intermediate values, confirms it was never built as a message queue. The deeper architectural cost shows up when repositories return `LiveData`. That pulls an Android lifecycle dependency into the data layer, leaving background services and workers without a `LifecycleOwner` to consume the data. Flow, by contrast, is a cold stream that consumers can collect, convert to state, or take a single value from, without the producer dictating the consumption pattern. The practical takeaway is not that LiveData is broken, but that its scope should shrink to the UI layer, where lifecycle awareness is the point, while Flow carries data across the rest of the app.
The shift from LiveData to Flow mirrors a broader industry move from value-holder patterns to stream-oriented architectures, where data is modeled as a continuous flow rather than a snapshot.
LiveData's tight coupling to Android lifecycle components made it a gateway drug for lifecycle safety, but that same coupling became a liability once apps needed to share data with non-UI consumers.
The SingleLiveEvent workaround is a symptom of a category error: LiveData was designed for state, not events, and the community spent years patching that mismatch instead of questioning the abstraction.
Exposing LiveData from a repository is an architectural decision that silently constrains every future consumer; switching to Flow is a one-time cost that pays off the first time a background task needs the same data.