Stateful journeys/App Widgets & Deep Links
hybridwidget selection session aggregate

Application-owned widget selection

Launch a Mappls widget, recover through a useful fallback, validate a narrow candidate, commit it deliberately, invalidate stale selection, and submit one durable business record.

Every state means something operational

A state is not a UI label. It determines which actor may act, what evidence exists, what may be retried, and how recovery proceeds.

01
draft

Draft

Host-owned address or place intent exists without an active provider surface or committed Mappls identity.

02
widget_open

Widget open

One launch generation owns the provider surface, lifecycle callbacks, focus, cancellation, and timeout.

03
fallback_active

Fallback active

The provider surface is unavailable and a bounded manual or search-assisted host path remains operable.

04
candidate_received

Candidate received

An exact-origin or native-adapter result passed schema validation but is not yet a business selection.

05
selected

Selected

The user deliberately committed a normalized Mappls Pin and label against the current host draft version.

06
submittedterminal

Submitted

Host text and committed selection form one immutable, attributable business record with an outbox event.

07
cancelledterminal

Cancelled

A named actor ended the journey without submission and with a recorded reason.

Transitions are attributable and retry-safe

Commands express intent. The aggregate validates current state and invariants, commits one new version, and emits a fact in the same transaction.

Command & actorAllowed fromResultIdempotency strategy
create_sessionHost application
new aggregate
drafthost.session_created

The external checkout/form identity maps to one aggregate across retries.

open_widgetApplication user
draftfallback_active
widget_openwidget.opened

Persist one launch generation and ignore every callback belonging to an older generation.

activate_fallbackPlatform adapter
widget_open
fallback_activewidget.fallback_activated

One failed generation produces at most one fallback transition and focus restoration.

receive_candidatePlatform adapter
widget_openfallback_active
candidate_receivedlocation.candidate_received

Deduplicate the terminal adapter result and bind it to session and launch generation.

accept_selectionApplication user
candidate_received
selectedlocation.selected

Compare aggregate version and hash the normalized candidate before committing.

edit_host_textApplication user
draftwidget_openfallback_activecandidate_receivedselected
drafthost.text_edited

The edit command version determines whether candidate and selection invalidation already occurred.

submitApplication user
selected
submittedhost.submitted

Commit the record, audit event, processed command, and outbox entry atomically.

cancelApplication user
draftwidget_openfallback_activecandidate_receivedselected
cancelledhost.cancelled

Repeated cancellation returns the terminal record without rerunning cleanup side effects.

Persist enough to recover and explain

Keep provider responses, business identity, state, events, and side-effect delivery distinct so each can be reconciled safely.

Selection session

Current host draft, lifecycle state, launch generation, candidate, selection, and optimistic version.

sessionIdexternalIdstateversionlaunchGenerationhostText

Normalized selection

Application-owned portable place identity independent of provider UI lifetime.

schemaVersionmapplsPinlabelsourceselectedAtselectedBy

Audit event

Attributable state transition and recovery history.

eventIdaggregateVersiontypeactoridempotencyKeyoccurredAt

Transactional outbox

Exactly-once-in-effect notification and downstream form processing.

outboxIdeventIdstatusattemptsnextAttemptAt

Protect truth at the aggregate boundary

  1. 1

    A provider callback or browser message creates only a candidate, never a submitted business record.

  2. 2

    Every browser message matches the exact reviewed origin and a versioned allow-listed schema.

  3. 3

    Only a six-character alphanumeric Mappls Pin and bounded printable label enter durable selection state.

  4. 4

    A host-text edit invalidates every candidate and committed selection from the previous draft version.

  5. 5

    One launch generation produces at most one terminal adapter outcome; late callbacks are ignored.

  6. 6

    Credentials, provider controllers, native views, bridge objects, and opaque response payloads are never persisted.

Know what to do before it breaks

Recovery changes durable truth only through the same rules as normal operation. A timeout is an unknown outcome, not evidence that nothing happened.

Widget fails, is denied, or times out

Detect

The active generation reaches a typed failure without a valid terminal candidate.

Recover

Dispose it, restore focus, record the reason, and activate a useful host-owned fallback.

Message arrives from a wrong origin or with unknown fields

Detect

Exact origin or narrow schema validation fails before domain processing.

Recover

Reject without changing aggregate state and emit a safe rejection metric without storing opaque content.

Callback arrives after screen disposal or a newer launch

Detect

Owner is inactive or result generation differs from the current session generation.

Recover

Ignore the late result and clean up its provider resources without committing state.

User edits the address after selecting a place

Detect

Host draft version changes while a candidate or selection exists.

Recover

Clear both values, return to draft, and require a new selection before submission.

Submit response is lost

Detect

Client lacks acknowledgement but retains session and idempotency identity.

Recover

Repeat the same command key or read the session; never create a second business record.

Observe the journey, not merely HTTP status

Measure state age, event health, retries, reconciliation, and sensitive-data access alongside latency and error rate.

  • Widget launch, time-to-active, and terminal outcome by platform and component version
  • Origin and schema rejection counts without raw payload retention
  • Fallback activation, completion, and abandonment rate
  • Candidate-to-selection and selection-to-submit conversion
  • Stale selection invalidation after host edits
  • Duplicate, late, and superseded callback count
  • Idempotency replay and optimistic version conflict rate
  • Outbox backlog, retry, and dead-letter age

Contracts, repositories, workshops, tutorials, and runnable evidence

Only explicitly indexed evidence is linked. Empty sections are not backfilled with invented endpoints or package names.