Migrate one complete screen at a time Store 5 screens and one migrated Store 6 screen live in one app, with independent caches connected to the same backend. ONE APP N screens Store 5 cache One migrated screen Store 6 cache Same backend Store 5 screens Migrated screen Shared backend Store 5 screens and one migrated Store 6 screen live in one app, with independent caches connected to the same backend.
Namespace ownership prevents leapfrogging An uncertain head generation in Namespace A blocks a later key, while an eligible head in Namespace B can push and retire. NAMESPACE A NAMESPACE B Head generation acceptance uncertain Later key blocked behind owner Eligible head Push and retire Lane owner Blocked work Eligible work An uncertain head generation in Namespace A blocks a later key, while an eligible head in Namespace B can push and retire.
When a canonical alias becomes active MutationStore persists an ACKED receipt and PENDING alias before adopting the echo, finishes declared invalidation effects, then retires the intent and activates the alias before the existing stream changes delegate. mutate( provisionalKey, createRef, args) push(provisional identity) PresentAck( canonicalKey) transaction: ACKED receipt + PENDING alias adopt authoritative echo finish declared invalidation effects transaction: retire intent + alias ACTIVE existing stream collection swaps to canonical delegate App MutationStore MutationServer Durable journal Call / local work Acknowledgement / return MutationStore persists an ACKED receipt and PENDING alias before adopting the echo, finishes declared invalidation effects, then retires the intent and activates the alias before the existing stream changes delegate.
Backend acceptance or precondition conflict MutationServer sends a deterministic request for generation g and idempotency key k, returns a Present or Absent acknowledgement after backend acceptance, or throws a sanctioned StoreResults.conflict after a precondition conflict. ALT [backend accepts] [precondition conflict] push(generation g, idempotencyKey k) deterministic request for g and k authoritative result Present or Absent acknowledgement conflict response sanctioned throw: StoreResults.conflict Mutation engine MutationServer Backend Call / local work Acknowledgement / return Sanctioned throw MutationServer sends a deterministic request for generation g and idempotency key k, returns a Present or Absent acknowledgement after backend acceptance, or throws a sanctioned StoreResults.conflict after a precondition conflict.
Recovery after backend acceptance A committed receipt and ACKED phase prevent another push of generation g; failure before that transaction commits leaves INFLIGHT, so a later drain replays g and k, receives the same result, persists the receipt, and resumes local work. ALT [receipt and ACKED transaction commits] [failure or death before commit] persist complete receipt and ACKED adopt echo, apply effects, retire intent Recovery may repeat local work, never push g Last durable phase remains INFLIGHT later drain replays generation g and key k duplicate request for g and k same authoritative result same acknowledgement persist complete receipt and ACKED resume adoption, effects, and retirement Mutation engine MutationServer Backend Call / local work Acknowledgement / return A committed receipt and ACKED phase prevent another push of generation g; failure before that transaction commits leaves INFLIGHT, so a later drain replays g and k, receives the same result, persists the receipt, and resumes local work.
Transactional acknowledgement: success Open the transaction, coalesce retirement signals, commit, apply and confirmFresh, restart collection, recapture the authoritative first row, deliver it, then release one retirement signal. SUCCESS / FOLLOW STEPS 01–08 01 Transaction open 02 Retirement signal coalesced 03 Commit 04 apply + confirmFresh 05 Collection restart 06 Authoritative first-row recapture 07 Row delivery 08 One retirement signal Ordered operation Commit and recapture boundaries Open the transaction, coalesce retirement signals, commit, apply and confirmFresh, restart collection, recapture the authoritative first row, deliver it, then release one retirement signal.
Transactional acknowledgement: rollback After transaction rollback, restart collection, recapture the authoritative first row, and discard retirement signals; confirmFresh is not an observation step. Transaction rollback Collection restart Authoritative first-row recapture Discard retirement signals confirmFresh is not an observation step Ordered operation Authoritative recapture After transaction rollback, restart collection, recapture the authoritative first row, and discard retirement signals; confirmFresh is not an observation step.
The persistence read path Memory can satisfy a read; otherwise SourceOfTruth.reader feeds stream emissions, and a policy-permitted fetch writes through SourceOfTruth so reader notifications can emit later values. READ THROUGH THE PERSISTENCE SEAM MISS OBSERVE ABSENT + POLICY FETCHES WRITE NOTIFY Read request Memory May satisfy read SourceOfTruth reader(key) Stream emissions Fetcher Successful value SourceOfTruth write(key, value) Persistence seam Stored value Notification can emit Memory can satisfy a read; otherwise SourceOfTruth.reader feeds stream emissions, and a policy-permitted fetch writes through SourceOfTruth so reader notifications can emit later values.
Single or multiple stores: coupled sources Tightly coupled data sources favor one store when atomic updates are required or combining data in one store is less complex; otherwise use multiple stores, and evaluate independent sources in part 2. 01 / START WITH COUPLING YES NO NO YES YES NO Are your data sources tightly coupled? Do you require atomic updates across data sources? Is combining data within a single store less complex? Independent sources Continue in part 2 Use a single store Use multiple stores Question Outcome Continue in part 2 Tightly coupled data sources favor one store when atomic updates are required or combining data in one store is less complex; otherwise use multiple stores, and evaluate independent sources in part 2.
Single or multiple stores: independent sources For independent sources, use multiple stores if caching or validation differs, error isolation matters, more sources are expected, or repository-level composition is feasible; otherwise use a single store. 02 / WHEN DATA SOURCES ARE NOT TIGHTLY COUPLED YES NO YES NO YES NO YES NO Do sources need different caching strategies or validation rules? Is independent error handling important? Do you anticipate adding more data sources in the future? Is combining data in the repository layer feasible? Use a single store Use multiple stores Question Outcome For independent sources, use multiple stores if caching or validation differs, error isolation matters, more sources are expected, or repository-level composition is feasible; otherwise use a single store.