Design: Offline-First Feed Flashcards
ADVANCED › System Design
- Why is the local database (Room) the single source of truth in an offline-first feed?
- The UI reads only from Room and all writes land in Room first. Because Room exposes observable Flows, consumers update automatically when data changes, reads work without a network, and consistency is guaranteed across connection states. Network is just a way to keep the DB fresh.
- Describe the network-bound resource flow for loading feed data.
- Immediately emit cached data from the DB; decide whether to fetch from the network; on success write the response into the DB, which re-emits to observers; on failure emit the cached data plus an error. The DB remains the source of truth and the UI never reads the network directly.
- What is RemoteMediator and how does it relate to PagingSource?
- PagingSource pages from the local DB. RemoteMediator is invoked when the DB runs out of cached pages (APPEND/PREPEND) or on REFRESH; it fetches network pages, writes them into the DB in a transaction, and the PagingSource then emits the new rows. It returns MediatorResult.Success(endOfPaginationReached) or Error.
- What is a RemoteKeys table and why do you need it?
- A separate Room table mapping items/pages to their prev/next page tokens. RemoteMediator reads it to know what to fetch on APPEND/PREPEND. On LoadType.REFRESH you clear items and keys, then insert the first page and its keys atomically in a single transaction.
- How does Paging 3 surface loading, empty, and error states?
- Via CombinedLoadStates: loadState.refresh, .append, .prepend each are Loading, NotLoading, or Error. Refresh Loading drives a full-screen shimmer; refresh Error with itemCount 0 is a full error+retry; NotLoading with endOfPaginationReached and itemCount 0 is the empty state; append Error shows a retry footer. Call retry() to re-attempt.
- Explain last-write-wins conflict resolution and its alternatives.
- Each write carries timestamp/version metadata; on sync the authoritative source discards data older than its current state and accepts newer writes. Alternatives: server-authoritative (server always wins), field-level merge, version vectors, or CRDTs for true mergeable state. Last-write-wins is simple but can silently drop concurrent edits.
- What are the three write strategies for an offline-first app?
- Online-only: write network first, update DB on success, fail if offline (e.g. bank transfer). Queued: enqueue and drain via WorkManager with backoff, best for non-critical writes like analytics. Local-first (lazy): write the DB immediately then sync later, best for likes/to-dos, and it requires conflict resolution.
- Why use WorkManager for feed sync and what does it guarantee?
- It persists work across process death and reboots, runs under constraints (e.g. NetworkType.CONNECTED), and dedupes via enqueueUniqueWork / unique periodic work. A CoroutineWorker returning Result.retry() gets automatic exponential backoff. It is the durable, OS-managed home for sync rather than an in-process coroutine.
- How would you test an offline-first feed?
- Use an in-memory Room DB with a fake network. Unit-test RemoteMediator.load() for REFRESH/APPEND, asserting it writes correct rows and RemoteKeys and returns the right endOfPaginationReached. Assert the source-of-truth Flow re-emits after sync (Turbine). Test WorkManager with WorkManagerTestInitHelper/TestDriver, plus conflict resolution and LoadState transitions.