Redesign data-load state model and normalize the Apollo cache#1
Open
solcott wants to merge 3 commits into
Open
Redesign data-load state model and normalize the Apollo cache#1solcott wants to merge 3 commits into
solcott wants to merge 3 commits into
Conversation
Rework the data-load state model to be more robust and reusable across data sources, and to carry richer error and origin information. model: - Replace Response<T> (Loading/Data/Error<String>) with Outcome<T>, a Data/Error result that drops Loading — an in-flight concern the consumer tracks, not a settled result. - Add DataError, a transport-neutral failure vocabulary (Network, Http, Api, Serialization, Unknown) so any client (Apollo, Ktor, Store) maps its own errors into a shared domain type. - Add Origin (Cache/Network), recorded on every Outcome. repository: - Map Apollo responses to Outcome via mapToOutcome, tagging Origin from isFromCache, categorizing ApolloExceptions into DataError, and dropping cache-miss emissions instead of surfacing them as errors. - Depend on apollo normalized-cache to read per-response cache metadata. - Remove the dead ContinentRepositoryImpl.kt. presenter: - Replace LoadState/CountriesState/ContinentsState with a single generic ContentState<T> plus a LoadStatus (Idle/Loading/Failed) sum type, eliminating the boolean-soup that allowed illegal states. - Derive Loading from the flow lifecycle (onEach/onCompletion), making no assumption about fetch policy; re-assert Loading on refilter via collectLatest while keeping the current list visible. - Move CountryDetailScreen.State onto ContentState<CountryDetail?> with a derived isNotFound. ui: - Resolve DataError to localized strings; show a RefreshingIndicator while reloading over already-visible data.
This change introduces client-side cache normalization policies to ensure that entity data is shared and kept consistent across different GraphQL queries. * **Cache Configuration**: Added `extra.graphqls` to define `@typePolicy` for `Country`, `Continent`, and `Language` types, using the `code` field as the unique cache key. * **GraphQL Queries**: Updated `Countries.graphql` and `CountryDetail.graphql` to explicitly select the `code` field for nested continent objects, satisfying the normalization requirement that key fields must be present in the selection set.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reworks how the app models the result of a data load and configures Apollo's
normalized cache so domain entities are shared across queries. Also bumps AGP.
The state redesign makes
Data/Errortotal and non-nullable, replaces aboolean-soup view state that allowed illegal combinations with a single generic
holder, and introduces a transport-agnostic error vocabulary so the model isn't
coupled to Apollo. The cache change is the plumbing that will let a future write
to one entity propagate to every screen that displays it.
Changes
model— transport-agnostic result typeResponse<T>(Loading/Data/Error) withOutcome<T>: aData(data, origin)/Error(cause, origin)result. Loading is no longer aresult case — it's an in-flight concern the consumer tracks.
DataError, a client-neutral failure vocabulary (Network,Http,Api,Serialization,Unknown) so any source (Apollo, Ktor, Store) maps itsown errors into a shared domain type.
Origin(Cache/Network), recorded on everyOutcome.repository— Apollo → Outcome adaptermapToOutcometags each emission'sOriginfromisFromCache, categorizesApolloExceptions intoDataError, and drops cache-miss emissions instead ofsurfacing them as errors.
apollo-normalized-cacheto read per-response cache metadata.ContinentRepositoryImpl.kt.presenter— one generic view-stateLoadState/CountriesState/ContinentsStatewith a single genericContentState<T>plus aLoadStatus(Idle/Loading/Failed) sumtype, eliminating illegal states.
onEach/onCompletion) with nofetch-policy assumption; re-assert Loading on refilter via
collectLatestwhile keeping the current list visible (stale-while-revalidate).
CountryDetailScreen.StateontoContentState<CountryDetail?>with aderived
isNotFound.uiDataErrorto localized strings; show a refresh indicator whilereloading over already-visible data.
network— normalized cache configuration@typePolicy(keyFields: "code")forCountry,Continent, andLanguageso every query shares one record per entity instead of embedded copies.
continent { code }in the Countries/CountryDetail queries, whichnormalization by key requires.
Build
Notes
one-shot
.toFlow(), not.watch(). This is the groundwork for cache-drivenreactivity (a follow-up would add
.watch()+ local cache writes).Testing
./gradlew assembleDebug test ktfmtCheck— all green.