Stateful journeys/Routes & Navigation
hybridtrip itinerary aggregate

Durable multi-stop itinerary

Discover provider-backed places, preserve their Mappls Pins in a versioned itinerary, preview the exact ordered route, invalidate stale plans on edits, and retain visit or skip progress through completion.

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

An ordered, bounded set of provider-backed places represents current trip intent without claiming a valid route.

02
planned

Planned

A route revision is bound to the exact ordered stop identities, travel profile, provider response, and planning time.

03
active

Active

The traveller started the current route revision and each next stop receives an explicit visited or skipped outcome.

04
paused

Paused

An attributable interruption stops progress without discarding saved stops, outcomes, or route identity.

05
completedterminal

Completed

Every stop is visited or explicitly skipped and the terminal itinerary, route revision, outcomes, and event history are retained.

06
cancelledterminal

Cancelled

A named actor ended the itinerary with a reason while retaining all committed place, route, and progress evidence.

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_tripTraveller
new aggregate
drafttrip.created

Use a client trip command ID before the first network attempt.

add_or_reorder_stopTraveller
draftplanned
drafttrip.sequence_changed

Compare trip version and bind every saved item to a unique Mappls Pin.

preview_routeMappls routing service
draft
plannedtrip.route_previewed

Hash ordered pins, profile, constraints, departure intent, and aggregate version.

start_tripTraveller
planned
activetrip.started

Bind start to the current trip and route revision.

pause_tripTraveller
active
pausedtrip.paused

Record the interruption reason once under a stable client command identity.

resume_tripTraveller
paused
activetrip.resumed

Bind resume to the paused aggregate and current route revision.

visit_or_skip_nextTraveller
active
activetrip.stop_completed

Generate one command identity per stop outcome; require a reason for skip.

complete_tripTraveller
active
completedtrip.completed

Terminal version and command ID identify one completion.

cancel_tripTraveller
draftplannedactivepaused
cancelledtrip.cancelled

One command records actor, reason, previous state, and terminal version.

Persist enough to recover and explain

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

Trip aggregate

Business identity, date, party, lifecycle, current route revision, and optimistic version.

tripIdstateversiondatepartySizerouteRevision

Itinerary stop

Ordered provider-backed place and progress outcome.

mapplsPinpositionproviderProvenancestatuscompletedAtskipReason

Route revision

Immutable preview for one exact intent version.

routeIdrevisionorderedPinsprofilelegsdistancedurationplannedAt

Audit and outbox

Attributable changes and reliable downstream collaboration or notification.

eventIdaggregateVersionactorcommandIdoutboxStatus

Protect truth at the aggregate boundary

  1. 1

    Every saved stop originated from provider-backed discovery and retains its Mappls Pin and provenance.

  2. 2

    A Mappls Pin occurs at most once in an itinerary.

  3. 3

    A route revision is valid only for the exact ordered stop list and constraints that produced it.

  4. 4

    Only the next pending stop may receive a visit or skip outcome.

  5. 5

    Completion is impossible while any stop remains pending.

  6. 6

    Commands are idempotent and compare the expected trip version.

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.

A collaborator edits after route preview

Detect

Current stop identities or aggregate version differ from the route's intent version.

Recover

Invalidate the route, show the edit, and require a new preview before start.

Routing succeeds but response is lost

Detect

The same ordered intent and request identity has no committed route revision.

Recover

Reconcile or repeat the same idempotent request; never attach a response to newer intent.

A venue becomes unavailable during the trip

Detect

Traveller or fresh provider/business data marks the next stop unavailable.

Recover

Record an explicit skip with reason, then offer a newly versioned replan from current context.

Application restarts mid-trip

Detect

A durable active aggregate exists without current client state.

Recover

Restore visit/skip progress and current revision, refresh stale operational data, and ask before resuming guidance.

Observe the journey, not merely HTTP status

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

  • Discovery-to-save rate and Mappls Pin continuity
  • Stop edits, duplicates, limits, and version conflicts
  • Route preview latency, failures, profiles, and revisions
  • Time from preview to start and stale-preview invalidation
  • Visited and skipped stops with reason
  • Active trips without recent progress
  • Idempotent replay, outbox backlog, and restart recovery

Contracts, repositories, workshops, tutorials, and runnable evidence

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