Bounded Context: Ride Management (RIDE)

Responsibility: The full ride lifecycle — request with committed fare, matching the closest eligible driver with offer rotation, pickup-to-completion with live tracking and ETA refresh, symmetric cancellation policy with published fees and driver penalties, and the safety bundle (trip sharing, emergency button, unusual-stop detection, post-ride reports).


Context Map

Relation Other Bounded Context Position Pattern Description
RIDE → GeolocationRouting Geolocation & Routing (GEO) downstream Customer-Supplier (consumer) RIDE consumes GEO’s nearby-driver query, ETA, route, and surge information through GeolocationGateway (an ACL). GEO is upstream and exposes an Open Host Service.
RIDE → Payment Payment (PAY) downstream Customer-Supplier (consumer) RIDE asks PAY for fare estimate, pre-auth, capture, refund, cancellation and no-show fee charges through PaymentGateway (an ACL).
RIDE → RiderManagement Rider Management (RDR) downstream Customer-Supplier (consumer) RIDE gates ride creation on the rider readiness check via RiderManagementGateway (an ACL).
RIDE → DriverManagement Driver Management (DRV) downstream Customer-Supplier (consumer) RIDE requests temporary suspensions via DriverManagementGateway (an ACL); RIDE also publishes DriverNoShowRecorded for DRV’s reliability score (Published Language).
RIDE ↔ GeolocationRouting Geolocation & Routing (GEO) partner Published Language RIDE subscribes to GEO’s ETAVarianceDetected, RouteRecalculated, SurgeActivated, SurgeDeactivated.
RIDE → Notification Notification downstream Conformist (infrastructure) All rider/driver/safety messaging goes through the RideNotificationService infrastructure boundary.

Enums

RideRequestStatus

  • Values: searching, matched, cancelled.
  • Realizes: REQ-RIDE-001, REQ-RIDE-006, REQ-RIDE-024, REQ-RIDE-025, REQ-RIDE-060.

RideStatus

  • Values: driverAssigned, driverArrived, inProgress, completed, cancelled.
  • Realizes: REQ-RIDE-025, REQ-RIDE-042, REQ-RIDE-044, REQ-RIDE-047, REQ-RIDE-061, REQ-RIDE-063, REQ-RIDE-065, REQ-RIDE-043.

RideOfferStatus

  • Values: pending, accepted, declined, timeout, retracted.
  • Realizes: REQ-RIDE-021, REQ-RIDE-023, REQ-RIDE-025.

CancellationReason

  • Values: riderBeforeAssignment, riderAfterAssignment, noDriverAvailable, driverCancelled, driverNoShow, riderNoShow, systemFault.
  • Realizes: REQ-RIDE-024, REQ-RIDE-043, REQ-RIDE-060, REQ-RIDE-061, REQ-RIDE-063, REQ-RIDE-065, REQ-RIDE-NFR-005.

SafetyReportSeverity

  • Values: low, medium, high, critical.
  • Realizes: REQ-RIDE-085.

EmergencyEventType

  • Values: riderEmergency, driverEmergency.
  • Realizes: REQ-RIDE-081.

RideScheduleType

  • Values: immediate, scheduled.
  • Realizes: REQ-RIDE-007, REQ-RIDE-008.

Value Types

Location

A point on Earth — local projection of GeoCoordinate from Geolocation.

  • Fields:
    • latitude: Decimal
    • longitude: Decimal

Route

Local projection of GEO’s Route — waypoints, distance, duration.

  • Fields:
    • waypoints: list<Location>
    • distance: Distance
    • duration: Duration

FareEstimate

Committed fare shown at confirm — the upfront-fare promise.

  • Fields:
    • amount: Amount
    • surgeMultiplier: Decimal — min(1.0)
    • distance: Distance
    • duration: Duration
    • committedAt: DateTime
  • Realizes: REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005, REQ-RIDE-NFR-002

FinalFare

Actual fare computed from real distance and duration at completion.

  • Fields:
    • amount: Amount
    • surgeMultiplier: Decimal — min(1.0)
    • actualDistance: Distance
    • actualDuration: Duration
    • computedAt: DateTime
  • Realizes: REQ-RIDE-048

CancellationDetail

Reason, party, fee outcome, and timestamp for any cancellation — the audit-trail record.

  • Fields:
    • reason: CancellationReason
    • cancelledBy: String
    • cancelledAt: DateTime
    • feeCharged: Boolean
    • feeAmount: Amount optional
  • Realizes: REQ-RIDE-060, REQ-RIDE-061, REQ-RIDE-062, REQ-RIDE-NFR-005

Short-lived live-tracking link delivered to a rider’s emergency contact.

  • Fields:
    • linkToken: String
    • contactName: String
    • contactPhone: PhoneNumber
    • activatedAt: DateTime
    • expiresAt: DateTime
  • Realizes: REQ-RIDE-080

PhoneNumber

  • Fields:
    • countryCode: String
    • number: String

Entities

RideOffer

Offer of a ride to a single driver — short-lived, deadline-bounded.

  • Identifier: offerId : UUID
  • Fields:
    • rideRequestId: UUID
    • driverId: UUID
    • status: RideOfferStatus — default pending
    • offeredAt: DateTime
    • respondedAt: DateTime optional
    • timeoutSeconds: Integer — default 15
  • Realizes: REQ-RIDE-021, REQ-RIDE-022, REQ-RIDE-023

RideOffer has no operations of its own — its mutations are driven by the temporal event RideOfferDeadlineElapsed and the reaction retractExpiredOffer, which issues HandleOfferTimeout to roll the offer to the next driver. The driver’s accept-or-decline arrives through the RideRequest.Match driver operation.

EmergencyEvent

Recorded emergency button press — timestamp, ride context, current location.

  • Identifier: emergencyId : UUID
  • Fields:
    • rideId: UUID
    • type: EmergencyEventType
    • triggeredBy: UUID
    • triggeredAt: DateTime
    • location: Location
    • acknowledgedAt: DateTime optional
    • resolvedAt: DateTime optional
  • Realizes: REQ-RIDE-081, REQ-RIDE-082, REQ-RIDE-083

EmergencyEvent has no named operations — instances are emitted by the Ride.Trigger emergency operation, which publishes EmergencyTriggered. The reactions notifySafetyTeamOnEmergency and shareWithEmergencyServicesIfConfigured carry the side-effects.

SafetyReport

Post-ride safety report attached to a ride record.

  • Identifier: reportId : UUID
  • Fields:
    • rideId: UUID
    • reportedBy: UUID
    • severity: SafetyReportSeverity
    • description: String — maxLength(4000)
    • reportedAt: DateTime
    • withinWindow: Boolean
    • escalated: Boolean — default false
    • escalatedAt: DateTime optional
  • Realizes: REQ-RIDE-085, REQ-RIDE-086

SafetyReport has no named operations — instances are emitted by the Ride.File post-ride safety report operation, which publishes SafetyReportFiled with a withinWindow flag computed from the 30-day rule.

RideRequest

Rider intent — destination, committed fare, matching state.

  • Identifier: requestId : UUID
  • Fields:
    • riderId: UUID
    • status: RideRequestStatus — default searching
    • scheduleType: RideScheduleType — default immediate
    • scheduledPickupTime: DateTime optional
    • pickupLocation: Location
    • dropoffLocation: Location
    • committedFare: FareEstimate
    • matchedDriverId: UUID optional
    • matchedAt: DateTime optional
    • cancellation: CancellationDetail optional
    • createdAt: DateTime
    • updatedAt: DateTime
  • Invariants:
    • scheduledHasFutureTime: when scheduleType = scheduled, scheduledPickupTime > now() — enforcement: reject
    • matchedHasDriver: when status = matched, matchedDriverId is defined — enforcement: reject
    • cancelledHasReason: when status = cancelled, cancellation is defined — enforcement: reject
  • Operations:
    • Request a ride on command RequestRide
      • Realizes: REQ-RIDE-001, REQ-RIDE-002, REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005
      • Preconditions:
        • riderReadyToRide: requestRide.riderReadyToRide = true
        • committedFareProvided: requestRide.committedFare is defined
        • paymentMethodValid: requestRide.paymentMethodValid = true
      • State changes: riderId, pickupLocation, dropoffLocation, committedFare, scheduleType, scheduledPickupTime, status = searching, createdAt = now(), updatedAt = now().
      • Emits: RideRequested with requestId, riderId, pickupLocation, dropoffLocation, committedFare, scheduleType, requestedAt.
    • Match driver on command AcceptRideOffer
      • Realizes: REQ-RIDE-021, REQ-RIDE-025
      • Precondition stillSearching: RideRequest.status = searching.
      • State changes: status = matched, matchedDriverId = acceptRideOffer.driverId, matchedAt = now().
      • Emits: RideRequestMatched with requestId, driverId, matchedAt.
    • Cancel request before assignment on command CancelRequestByRider
      • Realizes: REQ-RIDE-006, REQ-RIDE-060
      • Precondition stillSearching: RideRequest.status = searching.
      • State change: status = cancelled; cancellation = { reason: riderBeforeAssignment, cancelledBy: "rider", feeCharged: false }.
      • Emits: RideRequestCancelledByRider with requestId, riderId, cancelledAt.
    • Cancel request — no driver available on command CancelRequestNoDriver
      • Realizes: REQ-RIDE-024
      • Precondition searchExhausted: cancelRequestNoDriver.searchExhausted = true.
      • State change: status = cancelled; cancellation = { reason: noDriverAvailable, cancelledBy: "system", feeCharged: false }.
      • Emits: NoDriverAvailable with requestId, riderId, cancelledAt.

Ride

A ride from match through completion or cancellation. Owns live tracking, fare finalization, the symmetric cancellation policy, and the safety bundle.

  • Identifier: rideId : UUID
  • Fields:
    • rideRequestId: UUID
    • riderId: UUID
    • driverId: UUID
    • status: RideStatus — default driverAssigned
    • pickupLocation: Location
    • dropoffLocation: Location
    • committedFare: FareEstimate
    • estimatedDriverArrivalTime: DateTime
    • actualPickupLocation: Location optional
    • actualPickupTime: DateTime optional
    • actualDropoffLocation: Location optional
    • actualDropoffTime: DateTime optional
    • finalFare: FinalFare optional
    • currentRoute: Route optional
    • cancellation: CancellationDetail optional
    • tripSharingLinks: list<TripSharingLink>
    • createdAt: DateTime
    • updatedAt: DateTime
  • Invariants:
    • completedHasActuals: when status = completed, actualPickupTime, actualDropoffTime, and finalFare are defined — enforcement: reject
    • cancelledHasReason: when status = cancelled, cancellation is defined — enforcement: reject
  • Operations:
    • Create ride from matched request on command CreateRide
      • Realizes: REQ-RIDE-025
      • State changes: bind rideRequestId, riderId, driverId, pickupLocation, dropoffLocation, committedFare, estimatedDriverArrivalTime; status = driverAssigned; createdAt = now(); updatedAt = now().
      • Emits: RideCreated.
    • Driver arrives at pickup on command SignalDriverArrival
      • Realizes: REQ-RIDE-042
      • Preconditions:
        • isAssigned: Ride.status = driverAssigned
        • near100m: signalDriverArrival.distance.meters <= 100
      • State change: status = driverArrived.
      • Emits: DriverArrived.
    • Start ride on command StartRide
      • Realizes: REQ-RIDE-044
      • Precondition driverArrived: Ride.status = driverArrived.
      • State changes: status = inProgress, actualPickupLocation = startRide.pickupLocation, actualPickupTime = now().
      • Emits: RideStarted.
    • Update route during trip on command UpdateRideTracking
      • Realizes: REQ-RIDE-045, REQ-RIDE-046
      • Precondition inProgress: Ride.status = inProgress.
      • State change: currentRoute = updateRideTracking.route.
      • Emits: RideLocationUpdated.
    • Complete ride on command CompleteRide
      • Realizes: REQ-RIDE-047, REQ-RIDE-048, REQ-RIDE-049, REQ-RIDE-050
      • Precondition inProgress: Ride.status = inProgress.
      • State changes: status = completed, actualDropoffLocation = completeRide.dropoffLocation, actualDropoffTime = now().
      • Emits: RideCompleted with actual distance and duration.
    • Rider cancels after assignment on command CancelRideByRider
      • Realizes: REQ-RIDE-061
      • Precondition assignedOrArrived: Ride.status = driverAssigned or Ride.status = driverArrived.
      • State change: status = cancelled; cancellation = { reason: riderAfterAssignment, cancelledBy: "rider", feeCharged: true, feeAmount }.
      • Emits: RideCancelledByRider.
    • Driver cancels assignment on command CancelRideByDriver
      • Realizes: REQ-RIDE-063
      • Precondition isAssigned: Ride.status = driverAssigned.
      • State change: status = cancelled; cancellation = { reason: driverCancelled, cancelledBy: "driver", feeCharged: false }.
      • Emits: RideCancelledByDriver.
    • Cancel due to rider no-show on command CancelRideRiderNoShow
      • Realizes: REQ-RIDE-043
      • Preconditions:
        • driverArrived: Ride.status = driverArrived
        • fiveMinutesElapsedSinceArrival: now() - cancelRideRiderNoShow.arrivedAt > 5min
      • State change: status = cancelled; cancellation = { reason: riderNoShow, cancelledBy: "system", feeCharged: true, feeAmount }.
      • Emits: RideCancelledRiderNoShow.
    • Cancel due to driver no-show on command CancelRideDriverNoShow
      • Realizes: REQ-RIDE-065, REQ-RIDE-067
      • Preconditions:
        • isAssigned: Ride.status = driverAssigned
        • pastEtaPlusFive: now() > Ride.estimatedDriverArrivalTime + 5min
      • State change: status = cancelled; cancellation = { reason: driverNoShow, cancelledBy: "system", feeCharged: false }.
      • Emits: RideCancelledDriverNoShow.
    • Activate trip sharing on command ActivateTripSharing
      • Realizes: REQ-RIDE-080
      • Precondition isLive: Ride.status is one of driverAssigned, driverArrived, inProgress.
      • State change: append the new link to Ride.tripSharingLinks.
      • Emits: TripSharingActivated.
    • Trigger emergency on command TriggerEmergency
      • Realizes: REQ-RIDE-081, REQ-RIDE-082, REQ-RIDE-NFR-004
      • Emits: EmergencyTriggered with type, location, triggeredAt.
    • Detect unusual stop on command DetectUnusualStop
      • Realizes: REQ-RIDE-084
      • Preconditions:
        • inProgress: Ride.status = inProgress
        • stoppedFiveMinutesOffRoute: detectUnusualStop.stoppedSeconds > 300 and detectUnusualStop.offRoute = true
      • Emits: UnusualStopDetected.
    • File post-ride safety report on command FileSafetyReport
      • Realizes: REQ-RIDE-085, REQ-RIDE-086
      • Precondition rideCompleted: Ride.status = completed.
      • Emits: SafetyReportFiled with withinWindow = (now() - Ride.actualDropoffTime <= 30days).

Aggregates

RideRequestAggregate

  • Root: RideRequest
  • Contains: RideOffer
  • Realizes: REQ-RIDE-001, REQ-RIDE-021, REQ-RIDE-023, REQ-RIDE-024, REQ-RIDE-025, REQ-RIDE-060

RideAggregate

  • Root: Ride
  • Contains: EmergencyEvent, SafetyReport
  • Realizes: REQ-RIDE-040 through REQ-RIDE-067, REQ-RIDE-080 through REQ-RIDE-086

State Machines

RideRequestLifecycle on RideRequest

  • Start state: searching
  • States:
    • searching — no scoped invariant
    • matched — invariant hasDriver: RideRequest.matchedDriverId is defined
    • cancelled — invariant hasCancellation: RideRequest.cancellation is defined
  • Transitions:
    • searchingmatched on AcceptRideOffer
    • searchingcancelled on CancelRequestByRider
    • searchingcancelled on CancelRequestNoDriver
  • Final states: matched, cancelled

RideLifecycle on Ride

  • Start state: driverAssigned
  • States:
    • driverAssigned — no scoped invariant
    • driverArrived — no scoped invariant
    • inProgress — invariant hasPickupTime: Ride.actualPickupTime is defined
    • completed — invariant hasFinalFare: Ride.finalFare is defined
    • cancelled — invariant hasCancellation: Ride.cancellation is defined
  • Transitions:
    • driverAssigneddriverArrived on SignalDriverArrival
    • driverArrivedinProgress on StartRide
    • inProgresscompleted on CompleteRide
    • driverAssignedcancelled on CancelRideByRider
    • driverAssignedcancelled on CancelRideByDriver
    • driverAssignedcancelled on CancelRideDriverNoShow
    • driverArrivedcancelled on CancelRideByRider
    • driverArrivedcancelled on CancelRideRiderNoShow
  • Final states: completed, cancelled

Domain Services

MatchingService

Match a request to the closest eligible driver — proximity, offer rotation, timeout handling.

  • Operations:
    • findAndOfferDriver(rideRequest: RideRequest) : void
    • handleOfferTimeout(offerId: uuid) : void
    • getDriverAcceptanceRate(driverId: uuid) : decimal
  • Realizes: REQ-RIDE-020, REQ-RIDE-021, REQ-RIDE-023, REQ-RIDE-024, REQ-RIDE-026, REQ-RIDE-NFR-001

Infrastructure Services

GeolocationGateway

Anti-Corruption Layer to GeolocationRouting — proximity queries, ETAs, route, off-route detection.

  • Operations:
    • findNearbyEligibleDrivers(pickup: Location, radiusMeters: decimal) : list<DriverRef>
    • estimateDriverArrival(driverId: uuid, pickup: Location) : datetime
    • getCurrentLocation(rideId: uuid) : Location
    • getCurrentRoute(rideId: uuid) : Route
    • isStopOffRoute(rideId: uuid, stop: Location) : boolean
  • Realizes: REQ-RIDE-020, REQ-RIDE-040, REQ-RIDE-045, REQ-RIDE-046

PaymentGateway

Anti-Corruption Layer to Payment — fare estimate, pre-auth, capture, fees, refunds.

  • Operations:
    • computeFareEstimate(pickup: Location, dropoff: Location) : FareEstimate
    • computeFinalFare(rideRequestId: uuid, actualDistance: decimal, actualDuration: int) : FinalFare
    • preAuthorize(riderId: uuid, fare: FareEstimate) : void
    • capturePayment(rideId: uuid) : void
    • releaseHold(rideRequestId: uuid) : void
    • chargeCancellationFee(riderId: uuid, rideRequestId: uuid) : decimal
    • chargeNoShowFee(riderId: uuid, rideId: uuid) : decimal
  • Realizes: REQ-RIDE-003, REQ-RIDE-048, REQ-RIDE-049, REQ-RIDE-061

RiderManagementGateway

Anti-Corruption Layer to Rider Management — the ready-to-ride gate.

  • Operations:
    • isReadyToRide(riderId: uuid) : boolean
  • Realizes: REQ-RIDE-002

DriverManagementGateway

Anti-Corruption Layer to Driver Management — penalty suspensions and cancellation/no-show counters.

  • Operations:
    • requestTemporarySuspension(driverId: uuid, duration: Duration, reason: string) : void
    • getCancellationCount(driverId: uuid, windowHours: int) : int
    • getNoShowCount(driverId: uuid, windowHours: int) : int
  • Realizes: REQ-RIDE-064, REQ-RIDE-066

RideNotificationService

Outbound notifications — rider, driver, safety team, emergency services.

  • Operations:
    • notifyRider(riderId: uuid, message: string) : void
    • notifyDriver(driverId: uuid, message: string) : void
    • sendTrackingLink(contacts: list<PhoneNumber>, rideId: uuid) : void
    • notifySafetyTeam(rideId: uuid, eventType: string) : void
    • shareLocationWithEmergencyServices(rideId: uuid, location: Location) : void
    • sendSafetyCheck(riderId: uuid, rideId: uuid, stop: Location) : void
  • Realizes: REQ-RIDE-024, REQ-RIDE-042, REQ-RIDE-067, REQ-RIDE-080, REQ-RIDE-082, REQ-RIDE-084

Commands

Command Fields Realizes
RequestRide riderId, pickupLocation, dropoffLocation, committedFare, scheduleType, scheduledPickupTime?, riderReadyToRide, paymentMethodValid, correlationId REQ-RIDE-001, REQ-RIDE-002, REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005
AcceptRideOffer offerId, requestId, driverId, correlationId REQ-RIDE-021, REQ-RIDE-025
CancelRequestByRider requestId, riderId, correlationId REQ-RIDE-006, REQ-RIDE-060
CancelRequestNoDriver requestId, searchExhausted, correlationId REQ-RIDE-024
CreateRide requestId, riderId, driverId, pickupLocation, dropoffLocation, committedFare, estimatedDriverArrivalTime, correlationId REQ-RIDE-025
SignalDriverArrival rideId, distance, correlationId REQ-RIDE-042
StartRide rideId, pickupLocation, correlationId REQ-RIDE-044
UpdateRideTracking rideId, route, correlationId REQ-RIDE-045, REQ-RIDE-046
CompleteRide rideId, dropoffLocation, actualDistance, actualDuration, correlationId REQ-RIDE-047, REQ-RIDE-048, REQ-RIDE-049, REQ-RIDE-050
CancelRideByRider rideId, feeAmount, correlationId REQ-RIDE-061
CancelRideByDriver rideId, correlationId REQ-RIDE-063
CancelRideRiderNoShow rideId, arrivedAt, feeAmount, correlationId REQ-RIDE-043
CancelRideDriverNoShow rideId, correlationId REQ-RIDE-065, REQ-RIDE-067
ActivateTripSharing rideId, link, correlationId REQ-RIDE-080
TriggerEmergency rideId, type, location, correlationId REQ-RIDE-081, REQ-RIDE-082
DetectUnusualStop rideId, location, stoppedSeconds, offRoute, correlationId REQ-RIDE-084
FileSafetyReport rideId, reportedBy, severity, description, correlationId REQ-RIDE-085, REQ-RIDE-086
HandleOfferTimeout offerId, correlationId REQ-RIDE-023

Events

Internal events

Event Fields Realizes
RideRequested requestId, riderId, pickupLocation, dropoffLocation, committedFare, scheduleType, requestedAt REQ-RIDE-001
RideRequestMatched requestId, driverId, matchedAt REQ-RIDE-021, REQ-RIDE-025, REQ-RIDE-NFR-001
RideRequestCancelledByRider requestId, riderId, cancelledAt REQ-RIDE-006, REQ-RIDE-060
NoDriverAvailable requestId, riderId, cancelledAt REQ-RIDE-024
RideCreated rideId, requestId, riderId, driverId, estimatedArrival, createdAt REQ-RIDE-025
DriverArrived rideId, riderId, driverId, arrivedAt REQ-RIDE-042
RideStarted rideId, pickupLocation, startedAt REQ-RIDE-044
RideLocationUpdated rideId, route, updatedAt REQ-RIDE-045, REQ-RIDE-046
RideCancelledByRider rideId, riderId, feeAmount, cancelledAt REQ-RIDE-061
RideCancelledByDriver rideId, riderId, driverId, cancelledAt REQ-RIDE-063
RideCancelledRiderNoShow rideId, riderId, feeAmount, cancelledAt REQ-RIDE-043
RideCancelledDriverNoShow rideId, riderId, driverId, cancelledAt REQ-RIDE-065, REQ-RIDE-067
TripSharingActivated rideId, link, activatedAt REQ-RIDE-080
EmergencyTriggered rideId, riderId, driverId, type, location, triggeredAt REQ-RIDE-081, REQ-RIDE-082
UnusualStopDetected rideId, location, stoppedSeconds, detectedAt REQ-RIDE-084
SafetyReportFiled rideId, reportedBy, severity, description, withinWindow, filedAt REQ-RIDE-085, REQ-RIDE-086

Temporal events

Event Definition Guard Realizes
RideOfferDeadlineElapsed relative-to RideOffer.offeredAt offset 15s RideOffer.status = pending REQ-RIDE-023
RiderBoardingDeadlineElapsed relative-to DriverArrived offset 5min Ride.status = driverArrived REQ-RIDE-043
DriverArrivalDeadlineElapsed relative-to (Ride.estimatedDriverArrivalTime + 5min) Ride.status = driverAssigned REQ-RIDE-065
ScheduledRideMatchingDue relative-to (RideRequest.scheduledPickupTime - 15min) scheduleType = scheduled and status = searching REQ-RIDE-008
ScheduledRideReminderDue relative-to (RideRequest.scheduledPickupTime - 30min) scheduleType = scheduled REQ-RIDE-009

External-bound events (Published Language)

Event Fields Consumed by Realizes
RideCompleted rideId, riderId, driverId, actualDistance, actualDuration, riderRating?, driverRating?, completedAt Payment, Driver Management REQ-RIDE-047, REQ-RIDE-048
DriverNoShowRecorded driverId, rideId, recordedAt Driver Management (reliability score) REQ-RIDE-067

RideRequested, RideCancelledByRider, RideCancelledByDriver, RideCancelledDriverNoShow, EmergencyTriggered, and SafetyReportFiled are also exposed as Published Language events for downstream consumers (analytics, audit, trust & safety operators).

External events consumed

Event Source Used in
PaymentCaptured Payment Confirms capture after CompleteRide; closes the payment side of the lifecycle.
ETAVarianceDetected GeolocationRouting Triggers rider notification on significant ETA growth during a trip.

Reactions

startMatchingOnRequest

“Trigger matching as soon as the request is filed.”

  • Trigger: event RideRequested
  • Guard: event.scheduleType = immediate
  • Effect: MatchingService.findAndOfferDriver(rideRequest = resolve(event.requestId))
  • Realizes: REQ-RIDE-020

startMatchingOnScheduledLeadTime

“Begin matching scheduled rides 15 minutes early.”

  • Trigger: temporal event ScheduledRideMatchingDue
  • Effect: MatchingService.findAndOfferDriver(rideRequest = resolve(event.requestId))
  • Realizes: REQ-RIDE-008

createRideOnMatch

“Materialize a Ride when a driver accepts.”

  • Trigger: event RideRequestMatched
  • Effect: issue CreateRide populated with the request fields and estimatedDriverArrivalTime computed from GeolocationGateway.estimateDriverArrival.
  • Realizes: REQ-RIDE-025

retractExpiredOffer

“Retract pending offer at deadline and roll to next driver.”

  • Trigger: temporal event RideOfferDeadlineElapsed
  • Effect: HandleOfferTimeout(offerId = event.offerId)
  • Realizes: REQ-RIDE-023

cancelOnRiderNoShow

“Cancel ride if rider has not boarded 5 min after driver arrival.”

  • Trigger: temporal event RiderBoardingDeadlineElapsed
  • Effect: CancelRideRiderNoShow(rideId, arrivedAt = event.referenceTime, feeAmount = computeNoShowFee())
  • Realizes: REQ-RIDE-043

cancelOnDriverNoShow

“Cancel ride and rematch when driver fails to arrive.”

  • Trigger: temporal event DriverArrivalDeadlineElapsed
  • Effect: CancelRideDriverNoShow(rideId = event.rideId)
  • Realizes: REQ-RIDE-065, REQ-RIDE-067

rematchAfterDriverFault

“Re-enter matching after driver cancellation or no-show.”

  • Trigger: event RideCancelledDriverNoShow
  • Effect: issue RequestRide cloning the original request.
  • Realizes: REQ-RIDE-067

suspendOnExcessiveDriverCancellations

“Suspend driver for 30 min after >3 cancellations in 24h.”

  • Trigger: event RideCancelledByDriver
  • Guard: DriverManagementGateway.getCancellationCount(event.driverId, 24) > 3
  • Effect: DriverManagementGateway.requestTemporarySuspension(event.driverId, 30, "Excessive cancellations in 24h")
  • Realizes: REQ-RIDE-064

suspendOnExcessiveDriverNoShows

“Suspend driver for 1 hour after >2 no-shows in 24h.”

  • Trigger: event RideCancelledDriverNoShow
  • Guard: DriverManagementGateway.getNoShowCount(event.driverId, 24) > 2
  • Effect: DriverManagementGateway.requestTemporarySuspension(event.driverId, 60, "Excessive no-shows in 24h")
  • Realizes: REQ-RIDE-066

publishDriverNoShowForReliability

“Publish DriverNoShowRecorded for the Driver Management reliability score.”

  • Trigger: event RideCancelledDriverNoShow
  • Effect: publish DriverNoShowRecorded to Driver Management.
  • Realizes: REQ-RIDE-067

notifySafetyTeamOnEmergency

“Notify the safety team within 60s of emergency button press.”

  • Trigger: event EmergencyTriggered
  • Effect: RideNotificationService.notifySafetyTeam(event.rideId, event.type)
  • Realizes: REQ-RIDE-082

shareWithEmergencyServicesIfConfigured

“Share location with local emergency services where integrated.”

  • Trigger: event EmergencyTriggered
  • Guard: marketSupportsEmergencyIntegration(event.rideId)
  • Effect: RideNotificationService.shareLocationWithEmergencyServices(event.rideId, event.location)
  • Realizes: REQ-RIDE-083

safetyCheckOnUnusualStop

“Send safety check ping to rider on unusual stop.”

  • Trigger: event UnusualStopDetected
  • Effect: RideNotificationService.sendSafetyCheck(riderId, rideId, event.location)
  • Realizes: REQ-RIDE-084

notifyRiderOnDriverNoShow

“Tell the rider a new driver is being found.”

  • Trigger: event RideCancelledDriverNoShow
  • Effect: RideNotificationService.notifyRider(event.riderId, "Your driver did not arrive — we are finding a new driver")
  • Realizes: REQ-RIDE-067

Agreements (cross-aggregate)

RideRequestRideConsistency

"RideRequest.matched ↔ a Ride exists for that request."

  • Participants: RideRequest, Ride
  • Reconciliation: orphanRideOrUnmaterializedMatch“Detect requests in matched status without a Ride.”
    • Detection: RideRequest.status = matched and not exists Ride where rideRequestId = RideRequest.requestId for > 60s
    • Response: alert
    • Escalation: operatorReviewQueue, engineeringOnCall
  • Realizes: REQ-RIDE-025

The agreement covers the brief eventual-consistency window between RideRequestMatched (emitted by RideRequest.Match driver) and RideCreated (emitted by Ride.Create ride from matched request via the createRideOnMatch reaction). Any window longer than 60 seconds indicates a wedged reaction or a missing Ride, and is alerted to operators.


Traceability Matrix

Requirement Realized by
REQ-RIDE-001 RideRequest, op Request a ride, command RequestRide, event RideRequested
REQ-RIDE-002 RiderManagementGateway.isReadyToRide, op Request a ride (precondition riderReadyToRide)
REQ-RIDE-003 FareEstimate, PaymentGateway.computeFareEstimate, op Request a ride
REQ-RIDE-004 FareEstimate, op Request a ride (cross-context agreement with GEO REQ-GEO-024 — surge frozen at confirm)
REQ-RIDE-005 FareEstimate (breakdown fields)
REQ-RIDE-006 op Cancel request before assignment, event RideRequestCancelledByRider
REQ-RIDE-007 RideScheduleType, RideRequest.scheduledPickupTime
REQ-RIDE-008 temporal event ScheduledRideMatchingDue, reaction startMatchingOnScheduledLeadTime
REQ-RIDE-009 temporal event ScheduledRideReminderDue
REQ-RIDE-020 MatchingService.findAndOfferDriver, GeolocationGateway.findNearbyEligibleDrivers, reaction startMatchingOnRequest
REQ-RIDE-021 RideOffer, MatchingService, op Match driver
REQ-RIDE-022 RideOffer (offer payload composed by MatchingService)
REQ-RIDE-023 RideOffer.timeoutSeconds, temporal event RideOfferDeadlineElapsed, reaction retractExpiredOffer, command HandleOfferTimeout
REQ-RIDE-024 op Cancel request — no driver available, event NoDriverAvailable, RideNotificationService.notifyRider
REQ-RIDE-025 op Match driver, op Create ride from matched request, reaction createRideOnMatch, agreement RideRequestRideConsistency
REQ-RIDE-026 MatchingService.getDriverAcceptanceRate
REQ-RIDE-040 Ride (status driverAssigned/driverArrived), GeolocationGateway.getCurrentLocation
REQ-RIDE-041 GeolocationGateway.estimateDriverArrival
REQ-RIDE-042 op Driver arrives at pickup, event DriverArrived, RideNotificationService.notifyRider
REQ-RIDE-043 op Cancel due to rider no-show, temporal event RiderBoardingDeadlineElapsed, reaction cancelOnRiderNoShow, event RideCancelledRiderNoShow
REQ-RIDE-044 op Start ride, event RideStarted
REQ-RIDE-045 op Update route during trip, GeolocationGateway.getCurrentRoute, event RideLocationUpdated
REQ-RIDE-046 op Update route during trip (ETA refresh propagated to rider app)
REQ-RIDE-047 op Complete ride, event RideCompleted
REQ-RIDE-048 FinalFare, PaymentGateway.computeFinalFare, op Complete ride
REQ-RIDE-049 PaymentGateway.capturePayment, op Complete ride
REQ-RIDE-050 op Complete ride (driver-app earnings publish path)
REQ-RIDE-060 op Cancel request before assignment, PaymentGateway.releaseHold, event RideRequestCancelledByRider
REQ-RIDE-061 op Rider cancels after assignment, PaymentGateway.chargeCancellationFee, event RideCancelledByRider
REQ-RIDE-062 CancellationDetail (policy surfaced through PRD copy & rider app)
REQ-RIDE-063 op Driver cancels assignment, event RideCancelledByDriver
REQ-RIDE-064 reaction suspendOnExcessiveDriverCancellations, DriverManagementGateway.requestTemporarySuspension
REQ-RIDE-065 op Cancel due to driver no-show, temporal event DriverArrivalDeadlineElapsed, reaction cancelOnDriverNoShow, event RideCancelledDriverNoShow
REQ-RIDE-066 reaction suspendOnExcessiveDriverNoShows, DriverManagementGateway.requestTemporarySuspension
REQ-RIDE-067 reaction rematchAfterDriverFault, reaction notifyRiderOnDriverNoShow, reaction publishDriverNoShowForReliability, event DriverNoShowRecorded
REQ-RIDE-080 TripSharingLink, op Activate trip sharing, event TripSharingActivated, RideNotificationService.sendTrackingLink
REQ-RIDE-081 EmergencyEvent, op Trigger emergency, event EmergencyTriggered
REQ-RIDE-082 reaction notifySafetyTeamOnEmergency, RideNotificationService.notifySafetyTeam
REQ-RIDE-083 reaction shareWithEmergencyServicesIfConfigured, RideNotificationService.shareLocationWithEmergencyServices
REQ-RIDE-084 op Detect unusual stop, event UnusualStopDetected, reaction safetyCheckOnUnusualStop, GeolocationGateway.isStopOffRoute
REQ-RIDE-085 SafetyReport, op File post-ride safety report, event SafetyReportFiled
REQ-RIDE-086 event SafetyReportFiled (withinWindow field routes to manual-triage queue when false)
REQ-RIDE-NFR-001 MatchingService (latency budget, p50/p95)
REQ-RIDE-NFR-002 FareEstimate, PaymentGateway.computeFareEstimate (latency budget)
REQ-RIDE-NFR-003 RideLifecycle (completion rate operational metric)
REQ-RIDE-NFR-004 op Trigger emergency (availability budget on the emergency path)
REQ-RIDE-NFR-005 CancellationDetail, all cancellation events (append-only audit log policy)
REQ-RIDE-NFR-006 All gateways (deployment-independence contract — published inter-context schemas)

Concrete syntax (.domain DSL)

The full .domain source that this page narrates. Entity declarations, value types, commands, events, invariants, preconditions, postconditions, services, state machines, reactions, and agreements appear in parse order.

context RideManagement :: "Ride lifecycle — upfront fare, matching, pickup-to-completion, cancellation, safety" {
    requirements-source "ride-management.sysreq"

    enum RideRequestStatus { searching, matched, cancelled }
    enum RideStatus { driverAssigned, driverArrived, inProgress, completed, cancelled }
    enum RideOfferStatus { pending, accepted, declined, timeout, retracted }
    enum CancellationReason {
        riderBeforeAssignment, riderAfterAssignment, noDriverAvailable,
        driverCancelled, driverNoShow, riderNoShow, systemFault
    }
    enum SafetyReportSeverity { low, medium, high, critical }
    enum EmergencyEventType { riderEmergency, driverEmergency }
    enum RideScheduleType { immediate, scheduled }

    // -----------------------------------------------------------------
    // Value types
    // -----------------------------------------------------------------

    value Location { fields { latitude: decimal; longitude: decimal } }
    value Route { fields { waypoints: list<Location>; distance: Distance; duration: Duration } }

    value FareEstimate :: "Committed fare shown at confirm" {
        satisfies [REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005, REQ-RIDE-NFR-002]
        fields {
            amount : Amount
            surgeMultiplier : decimal min(1.0)
            distance : Distance
            duration : Duration
            committedAt : datetime
        }
    }

    value FinalFare {
        satisfies [REQ-RIDE-048]
        fields {
            amount : Amount
            surgeMultiplier : decimal min(1.0)
            actualDistance : Distance
            actualDuration : Duration
            computedAt : datetime
        }
    }

    value CancellationDetail {
        satisfies [REQ-RIDE-060, REQ-RIDE-061, REQ-RIDE-062, REQ-RIDE-NFR-005]
        fields {
            reason : CancellationReason
            cancelledBy : string
            cancelledAt : datetime
            feeCharged : boolean
            feeAmount : Amount optional
        }
    }

    value TripSharingLink {
        satisfies [REQ-RIDE-080]
        fields {
            linkToken : string
            contactName : string
            contactPhone : PhoneNumber
            activatedAt : datetime
            expiresAt : datetime
        }
    }

    value PhoneNumber { fields { countryCode: string; number: string } }

    // -----------------------------------------------------------------
    // Entities
    // -----------------------------------------------------------------

    entity RideOffer :: "Offer of a ride to a single driver" {
        satisfies [REQ-RIDE-021, REQ-RIDE-022, REQ-RIDE-023]
        identifier offerId : UUID
        fields {
            rideRequestId : UUID
            driverId : UUID
            status : RideOfferStatus default("pending")
            offeredAt : datetime
            respondedAt : datetime optional
            timeoutSeconds : int default(15)
        }
    }

    entity EmergencyEvent {
        satisfies [REQ-RIDE-081, REQ-RIDE-082, REQ-RIDE-083]
        identifier emergencyId : UUID
        fields {
            rideId : UUID
            type : EmergencyEventType
            triggeredBy : UUID
            triggeredAt : datetime
            location : Location
            acknowledgedAt : datetime optional
            resolvedAt : datetime optional
        }
    }

    entity SafetyReport {
        satisfies [REQ-RIDE-085, REQ-RIDE-086]
        identifier reportId : UUID
        fields {
            rideId : UUID
            reportedBy : UUID
            severity : SafetyReportSeverity
            description : string maxLength(4000)
            reportedAt : datetime
            withinWindow : boolean
            escalated : boolean default(false)
            escalatedAt : datetime optional
        }
    }

    entity RideRequest :: "Rider intent — destination, fare, matching state" {
        satisfies [REQ-RIDE-001, REQ-RIDE-002, REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005, REQ-RIDE-006,
                   REQ-RIDE-007, REQ-RIDE-008, REQ-RIDE-009, REQ-RIDE-020, REQ-RIDE-024, REQ-RIDE-025,
                   REQ-RIDE-060, REQ-RIDE-NFR-001, REQ-RIDE-NFR-002]

        identifier requestId : UUID
        fields {
            riderId : UUID
            status : RideRequestStatus default("searching")
            scheduleType : RideScheduleType default("immediate")
            scheduledPickupTime : datetime optional
            pickupLocation : Location
            dropoffLocation : Location
            committedFare : FareEstimate
            matchedDriverId : UUID optional
            matchedAt : datetime optional
            cancellation : CancellationDetail optional
            createdAt : datetime
            updatedAt : datetime
        }

        invariants {
            scheduledHasFutureTime :: "Scheduled rides have a future pickup time" enforcement reject {
                if scheduleType = scheduled { scheduledPickupTime > now() }
            }
            matchedHasDriver :: "Matched requests have a driver" enforcement reject {
                if status = matched { matchedDriverId is defined }
            }
            cancelledHasReason :: "Cancelled requests record a reason" enforcement reject {
                if status = cancelled { cancellation is defined }
            }
        }

        operations {
            "Request a ride" on RequestRide {
                satisfies [REQ-RIDE-001, REQ-RIDE-002, REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005]
                precondition riderReadyToRide { requestRide.riderReadyToRide = true }
                precondition committedFareProvided { requestRide.committedFare is defined }
                precondition paymentMethodValid { requestRide.paymentMethodValid = true }
                sets RideRequest {
                    riderId = requestRide.riderId
                    pickupLocation = requestRide.pickupLocation
                    dropoffLocation = requestRide.dropoffLocation
                    committedFare = requestRide.committedFare
                    scheduleType = requestRide.scheduleType
                    scheduledPickupTime = requestRide.scheduledPickupTime
                    status = searching
                    createdAt = now()
                    updatedAt = now()
                }
                emits RideRequested {
                    requestId = RideRequest.requestId
                    riderId = RideRequest.riderId
                    pickupLocation = RideRequest.pickupLocation
                    dropoffLocation = RideRequest.dropoffLocation
                    committedFare = RideRequest.committedFare
                    scheduleType = RideRequest.scheduleType
                    requestedAt = RideRequest.createdAt
                }
            }

            "Match driver" on AcceptRideOffer {
                satisfies [REQ-RIDE-021, REQ-RIDE-025]
                precondition stillSearching { RideRequest.status = searching }
                sets RideRequest {
                    status = matched
                    matchedDriverId = acceptRideOffer.driverId
                    matchedAt = now()
                }
                emits RideRequestMatched {
                    requestId = RideRequest.requestId
                    driverId = RideRequest.matchedDriverId
                    matchedAt = RideRequest.matchedAt
                }
            }

            "Cancel request before assignment" on CancelRequestByRider {
                satisfies [REQ-RIDE-006, REQ-RIDE-060]
                precondition stillSearching { RideRequest.status = searching }
                sets RideRequest {
                    status = cancelled
                    cancellation = CancellationDetail(
                        reason = riderBeforeAssignment,
                        cancelledBy = "rider",
                        cancelledAt = now(),
                        feeCharged = false
                    )
                }
                emits RideRequestCancelledByRider {
                    requestId = RideRequest.requestId
                    riderId = RideRequest.riderId
                    cancelledAt = now()
                }
            }

            "Cancel request — no driver available" on CancelRequestNoDriver {
                satisfies [REQ-RIDE-024]
                precondition searchExhausted { cancelRequestNoDriver.searchExhausted = true }
                sets RideRequest {
                    status = cancelled
                    cancellation = CancellationDetail(
                        reason = noDriverAvailable,
                        cancelledBy = "system",
                        cancelledAt = now(),
                        feeCharged = false
                    )
                }
                emits NoDriverAvailable {
                    requestId = RideRequest.requestId
                    riderId = RideRequest.riderId
                    cancelledAt = now()
                }
            }
        }
    }

    entity Ride :: "Ride from match through completion or cancellation" {
        satisfies [REQ-RIDE-040, REQ-RIDE-041, REQ-RIDE-042, REQ-RIDE-043, REQ-RIDE-044, REQ-RIDE-045,
                   REQ-RIDE-046, REQ-RIDE-047, REQ-RIDE-048, REQ-RIDE-049, REQ-RIDE-050,
                   REQ-RIDE-061, REQ-RIDE-063, REQ-RIDE-065, REQ-RIDE-067,
                   REQ-RIDE-080, REQ-RIDE-081, REQ-RIDE-082, REQ-RIDE-084, REQ-RIDE-085]

        identifier rideId : UUID
        fields {
            rideRequestId : UUID
            riderId : UUID
            driverId : UUID
            status : RideStatus default("driverAssigned")
            pickupLocation : Location
            dropoffLocation : Location
            committedFare : FareEstimate
            estimatedDriverArrivalTime : datetime
            actualPickupLocation : Location optional
            actualPickupTime : datetime optional
            actualDropoffLocation : Location optional
            actualDropoffTime : datetime optional
            finalFare : FinalFare optional
            currentRoute : Route optional
            cancellation : CancellationDetail optional
            tripSharingLinks : list<TripSharingLink>
            createdAt : datetime
            updatedAt : datetime
        }

        invariants {
            completedHasActuals :: "Completed rides have actual times and final fare" enforcement reject {
                if status = completed {
                    actualPickupTime is defined
                    and actualDropoffTime is defined
                    and finalFare is defined
                }
            }
            cancelledHasReason :: "Cancelled rides record a reason" enforcement reject {
                if status = cancelled { cancellation is defined }
            }
        }

        operations {
            "Create ride from matched request" on CreateRide {
                satisfies [REQ-RIDE-025]
                sets Ride {
                    rideRequestId = createRide.requestId
                    riderId = createRide.riderId
                    driverId = createRide.driverId
                    pickupLocation = createRide.pickupLocation
                    dropoffLocation = createRide.dropoffLocation
                    committedFare = createRide.committedFare
                    estimatedDriverArrivalTime = createRide.estimatedDriverArrivalTime
                    status = driverAssigned
                    createdAt = now()
                    updatedAt = now()
                }
                emits RideCreated {
                    rideId = Ride.rideId
                    requestId = Ride.rideRequestId
                    riderId = Ride.riderId
                    driverId = Ride.driverId
                    estimatedArrival = Ride.estimatedDriverArrivalTime
                    createdAt = Ride.createdAt
                }
            }

            "Driver arrives at pickup" on SignalDriverArrival {
                satisfies [REQ-RIDE-042]
                precondition isAssigned { Ride.status = driverAssigned }
                precondition near100m { signalDriverArrival.distance.meters <= 100 }
                sets Ride { status = driverArrived }
                emits DriverArrived {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    driverId = Ride.driverId
                    arrivedAt = now()
                }
            }

            "Start ride" on StartRide {
                satisfies [REQ-RIDE-044]
                precondition driverArrived { Ride.status = driverArrived }
                sets Ride {
                    status = inProgress
                    actualPickupLocation = startRide.pickupLocation
                    actualPickupTime = now()
                }
                emits RideStarted {
                    rideId = Ride.rideId
                    pickupLocation = Ride.actualPickupLocation
                    startedAt = Ride.actualPickupTime
                }
            }

            "Update route during trip" on UpdateRideTracking {
                satisfies [REQ-RIDE-045, REQ-RIDE-046]
                precondition inProgress { Ride.status = inProgress }
                sets Ride { currentRoute = updateRideTracking.route }
                emits RideLocationUpdated {
                    rideId = Ride.rideId
                    route = Ride.currentRoute
                    updatedAt = now()
                }
            }

            "Complete ride" on CompleteRide {
                satisfies [REQ-RIDE-047, REQ-RIDE-048, REQ-RIDE-049, REQ-RIDE-050]
                precondition inProgress { Ride.status = inProgress }
                sets Ride {
                    status = completed
                    actualDropoffLocation = completeRide.dropoffLocation
                    actualDropoffTime = now()
                }
                emits RideCompleted {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    driverId = Ride.driverId
                    actualDistance = completeRide.actualDistance
                    actualDuration = completeRide.actualDuration
                    completedAt = Ride.actualDropoffTime
                }
            }

            "Rider cancels after assignment" on CancelRideByRider {
                satisfies [REQ-RIDE-061]
                precondition assignedOrArrived {
                    Ride.status = driverAssigned or Ride.status = driverArrived
                }
                sets Ride {
                    status = cancelled
                    cancellation = CancellationDetail(
                        reason = riderAfterAssignment,
                        cancelledBy = "rider",
                        cancelledAt = now(),
                        feeCharged = true,
                        feeAmount = cancelRideByRider.feeAmount
                    )
                }
                emits RideCancelledByRider {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    feeAmount = Ride.cancellation.feeAmount
                    cancelledAt = now()
                }
            }

            "Driver cancels assignment" on CancelRideByDriver {
                satisfies [REQ-RIDE-063]
                precondition isAssigned { Ride.status = driverAssigned }
                sets Ride {
                    status = cancelled
                    cancellation = CancellationDetail(
                        reason = driverCancelled,
                        cancelledBy = "driver",
                        cancelledAt = now(),
                        feeCharged = false
                    )
                }
                emits RideCancelledByDriver {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    driverId = Ride.driverId
                    cancelledAt = now()
                }
            }

            "Cancel due to rider no-show" on CancelRideRiderNoShow {
                satisfies [REQ-RIDE-043]
                precondition driverArrived { Ride.status = driverArrived }
                precondition fiveMinutesElapsedSinceArrival {
                    now() - cancelRideRiderNoShow.arrivedAt > 5min
                }
                sets Ride {
                    status = cancelled
                    cancellation = CancellationDetail(
                        reason = riderNoShow,
                        cancelledBy = "system",
                        cancelledAt = now(),
                        feeCharged = true,
                        feeAmount = cancelRideRiderNoShow.feeAmount
                    )
                }
                emits RideCancelledRiderNoShow {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    feeAmount = Ride.cancellation.feeAmount
                    cancelledAt = now()
                }
            }

            "Cancel due to driver no-show" on CancelRideDriverNoShow {
                satisfies [REQ-RIDE-065, REQ-RIDE-067]
                precondition isAssigned { Ride.status = driverAssigned }
                precondition pastEtaPlusFive {
                    now() > Ride.estimatedDriverArrivalTime + 5min
                }
                sets Ride {
                    status = cancelled
                    cancellation = CancellationDetail(
                        reason = driverNoShow,
                        cancelledBy = "system",
                        cancelledAt = now(),
                        feeCharged = false
                    )
                }
                emits RideCancelledDriverNoShow {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    driverId = Ride.driverId
                    cancelledAt = now()
                }
            }

            "Activate trip sharing" on ActivateTripSharing {
                satisfies [REQ-RIDE-080]
                precondition isLive {
                    Ride.status = driverAssigned
                    or Ride.status = driverArrived
                    or Ride.status = inProgress
                }
                sets Ride { tripSharingLinks = Ride.tripSharingLinks + activateTripSharing.link }
                emits TripSharingActivated {
                    rideId = Ride.rideId
                    link = activateTripSharing.link
                    activatedAt = now()
                }
            }

            "Trigger emergency" on TriggerEmergency {
                satisfies [REQ-RIDE-081, REQ-RIDE-082, REQ-RIDE-NFR-004]
                emits EmergencyTriggered {
                    rideId = Ride.rideId
                    riderId = Ride.riderId
                    driverId = Ride.driverId
                    type = triggerEmergency.type
                    location = triggerEmergency.location
                    triggeredAt = now()
                }
            }

            "Detect unusual stop" on DetectUnusualStop {
                satisfies [REQ-RIDE-084]
                precondition inProgress { Ride.status = inProgress }
                precondition stoppedFiveMinutesOffRoute {
                    detectUnusualStop.stoppedSeconds > 300
                    and detectUnusualStop.offRoute = true
                }
                emits UnusualStopDetected {
                    rideId = Ride.rideId
                    location = detectUnusualStop.location
                    stoppedSeconds = detectUnusualStop.stoppedSeconds
                    detectedAt = now()
                }
            }

            "File post-ride safety report" on FileSafetyReport {
                satisfies [REQ-RIDE-085, REQ-RIDE-086]
                precondition rideCompleted { Ride.status = completed }
                emits SafetyReportFiled {
                    rideId = Ride.rideId
                    reportedBy = fileSafetyReport.reportedBy
                    severity = fileSafetyReport.severity
                    description = fileSafetyReport.description
                    withinWindow = (now() - Ride.actualDropoffTime <= 30days)
                    filedAt = now()
                }
            }
        }
    }

    aggregate RideRequestAggregate root RideRequest { contains [RideOffer] }
    aggregate RideAggregate root Ride { contains [EmergencyEvent, SafetyReport] }

    statemachine RideRequestLifecycle on RideRequest {
        start searching
        state searching {}
        state matched {
            invariant hasDriver { RideRequest.matchedDriverId is defined }
        }
        state cancelled {
            invariant hasCancellation { RideRequest.cancellation is defined }
        }
        transition searching -> matched on AcceptRideOffer
        transition searching -> cancelled on CancelRequestByRider
        transition searching -> cancelled on CancelRequestNoDriver
        final matched
        final cancelled
    }

    statemachine RideLifecycle on Ride {
        start driverAssigned
        state driverAssigned {}
        state driverArrived {}
        state inProgress {
            invariant hasPickupTime { Ride.actualPickupTime is defined }
        }
        state completed {
            invariant hasFinalFare { Ride.finalFare is defined }
        }
        state cancelled {
            invariant hasCancellation { Ride.cancellation is defined }
        }
        transition driverAssigned -> driverArrived on SignalDriverArrival
        transition driverArrived -> inProgress on StartRide
        transition inProgress -> completed on CompleteRide
        transition driverAssigned -> cancelled on CancelRideByRider
        transition driverAssigned -> cancelled on CancelRideByDriver
        transition driverAssigned -> cancelled on CancelRideDriverNoShow
        transition driverArrived -> cancelled on CancelRideByRider
        transition driverArrived -> cancelled on CancelRideRiderNoShow
        final completed
        final cancelled
    }

    // -----------------------------------------------------------------
    // Domain services
    // -----------------------------------------------------------------

    service MatchingService :: "Match a request to the closest eligible driver" {
        satisfies [REQ-RIDE-020, REQ-RIDE-021, REQ-RIDE-023, REQ-RIDE-024, REQ-RIDE-026, REQ-RIDE-NFR-001]
        operations {
            findAndOfferDriver(rideRequest: RideRequest) : void
            handleOfferTimeout(offerId: uuid) : void
            getDriverAcceptanceRate(driverId: uuid) : decimal
        }
    }

    // -----------------------------------------------------------------
    // Infrastructure services (anti-corruption boundaries)
    // -----------------------------------------------------------------

    infrastructure-service GeolocationGateway :: "ACL to GeolocationRouting" {
        satisfies [REQ-RIDE-020, REQ-RIDE-040, REQ-RIDE-045, REQ-RIDE-046]
        operations {
            findNearbyEligibleDrivers(pickup: Location, radius: Distance) : list<DriverRef>
            estimateDriverArrival(driverId: uuid, pickup: Location) : datetime
            getCurrentLocation(rideId: uuid) : Location
            getCurrentRoute(rideId: uuid) : Route
            isStopOffRoute(rideId: uuid, stop: Location) : boolean
        }
    }

    infrastructure-service PaymentGateway :: "ACL to Payment" {
        satisfies [REQ-RIDE-003, REQ-RIDE-048, REQ-RIDE-049, REQ-RIDE-061]
        operations {
            computeFareEstimate(pickup: Location, dropoff: Location) : FareEstimate
            computeFinalFare(rideRequestId: uuid, actualDistance: Distance, actualDuration: Duration) : FinalFare
            preAuthorize(riderId: uuid, fare: FareEstimate) : void
            capturePayment(rideId: uuid) : void
            releaseHold(rideRequestId: uuid) : void
            chargeCancellationFee(riderId: uuid, rideRequestId: uuid) : Amount
            chargeNoShowFee(riderId: uuid, rideId: uuid) : Amount
        }
    }

    infrastructure-service RiderManagementGateway :: "ACL to RiderManagement" {
        satisfies [REQ-RIDE-002]
        operations {
            isReadyToRide(riderId: uuid) : boolean
        }
    }

    infrastructure-service DriverManagementGateway :: "ACL to DriverManagement" {
        satisfies [REQ-RIDE-064, REQ-RIDE-066]
        operations {
            requestTemporarySuspension(driverId: uuid, duration: Duration, reason: string) : void
            getCancellationCount(driverId: uuid, windowHours: int) : int
            getNoShowCount(driverId: uuid, windowHours: int) : int
        }
    }

    infrastructure-service RideNotificationService {
        satisfies [REQ-RIDE-024, REQ-RIDE-042, REQ-RIDE-067, REQ-RIDE-080, REQ-RIDE-082, REQ-RIDE-084]
        operations {
            notifyRider(riderId: uuid, message: string) : void
            notifyDriver(driverId: uuid, message: string) : void
            sendTrackingLink(contacts: list<PhoneNumber>, rideId: uuid) : void
            notifySafetyTeam(rideId: uuid, eventType: string) : void
            shareLocationWithEmergencyServices(rideId: uuid, location: Location) : void
            sendSafetyCheck(riderId: uuid, rideId: uuid, stop: Location) : void
        }
    }

    // -----------------------------------------------------------------
    // Commands
    // -----------------------------------------------------------------

    command RequestRide { fields { riderId: uuid; pickupLocation: Location; dropoffLocation: Location; committedFare: FareEstimate; scheduleType: RideScheduleType; scheduledPickupTime: datetime optional; riderReadyToRide: boolean; paymentMethodValid: boolean; correlationId: uuid } }
    command AcceptRideOffer { fields { offerId: uuid; requestId: uuid; driverId: uuid; correlationId: uuid } }
    command CancelRequestByRider { fields { requestId: uuid; riderId: uuid; correlationId: uuid } }
    command CancelRequestNoDriver { fields { requestId: uuid; searchExhausted: boolean; correlationId: uuid } }
    command CreateRide { fields { requestId: uuid; riderId: uuid; driverId: uuid; pickupLocation: Location; dropoffLocation: Location; committedFare: FareEstimate; estimatedDriverArrivalTime: datetime; correlationId: uuid } }
    command SignalDriverArrival { fields { rideId: uuid; distance: Distance; correlationId: uuid } }
    command StartRide { fields { rideId: uuid; pickupLocation: Location; correlationId: uuid } }
    command UpdateRideTracking { fields { rideId: uuid; route: Route; correlationId: uuid } }
    command CompleteRide { fields { rideId: uuid; dropoffLocation: Location; actualDistance: Distance; actualDuration: Duration; correlationId: uuid } }
    command CancelRideByRider { fields { rideId: uuid; feeAmount: Amount; correlationId: uuid } }
    command CancelRideByDriver { fields { rideId: uuid; correlationId: uuid } }
    command CancelRideRiderNoShow { fields { rideId: uuid; arrivedAt: datetime; feeAmount: Amount; correlationId: uuid } }
    command CancelRideDriverNoShow { fields { rideId: uuid; correlationId: uuid } }
    command ActivateTripSharing { fields { rideId: uuid; link: TripSharingLink; correlationId: uuid } }
    command TriggerEmergency { fields { rideId: uuid; type: EmergencyEventType; location: Location; correlationId: uuid } }
    command DetectUnusualStop { fields { rideId: uuid; location: Location; stoppedSeconds: int; offRoute: boolean; correlationId: uuid } }
    command FileSafetyReport { fields { rideId: uuid; reportedBy: uuid; severity: SafetyReportSeverity; description: string; correlationId: uuid } }
    command HandleOfferTimeout { fields { offerId: uuid; correlationId: uuid } }

    // -----------------------------------------------------------------
    // Events
    // -----------------------------------------------------------------

    event RideRequested { satisfies [REQ-RIDE-001] fields { requestId: uuid; riderId: uuid; pickupLocation: Location; dropoffLocation: Location; committedFare: FareEstimate; scheduleType: RideScheduleType; requestedAt: datetime } }
    event RideRequestMatched { satisfies [REQ-RIDE-021, REQ-RIDE-025, REQ-RIDE-NFR-001] fields { requestId: uuid; driverId: uuid; matchedAt: datetime } }
    event RideRequestCancelledByRider { satisfies [REQ-RIDE-006, REQ-RIDE-060] fields { requestId: uuid; riderId: uuid; cancelledAt: datetime } }
    event NoDriverAvailable { satisfies [REQ-RIDE-024] fields { requestId: uuid; riderId: uuid; cancelledAt: datetime } }
    event RideCreated { satisfies [REQ-RIDE-025] fields { rideId: uuid; requestId: uuid; riderId: uuid; driverId: uuid; estimatedArrival: datetime; createdAt: datetime } }
    event DriverArrived { satisfies [REQ-RIDE-042] fields { rideId: uuid; riderId: uuid; driverId: uuid; arrivedAt: datetime } }
    event RideStarted { satisfies [REQ-RIDE-044] fields { rideId: uuid; pickupLocation: Location; startedAt: datetime } }
    event RideLocationUpdated { satisfies [REQ-RIDE-045, REQ-RIDE-046] fields { rideId: uuid; route: Route; updatedAt: datetime } }
    event RideCompleted { satisfies [REQ-RIDE-047, REQ-RIDE-048] fields { rideId: uuid; riderId: uuid; driverId: uuid; actualDistance: Distance; actualDuration: Duration; riderRating: any optional; driverRating: any optional; completedAt: datetime } }
    event RideCancelledByRider { satisfies [REQ-RIDE-061] fields { rideId: uuid; riderId: uuid; feeAmount: Amount; cancelledAt: datetime } }
    event RideCancelledByDriver { satisfies [REQ-RIDE-063] fields { rideId: uuid; riderId: uuid; driverId: uuid; cancelledAt: datetime } }
    event RideCancelledRiderNoShow { satisfies [REQ-RIDE-043] fields { rideId: uuid; riderId: uuid; feeAmount: Amount; cancelledAt: datetime } }
    event RideCancelledDriverNoShow { satisfies [REQ-RIDE-065, REQ-RIDE-067] fields { rideId: uuid; riderId: uuid; driverId: uuid; cancelledAt: datetime } }
    event TripSharingActivated { satisfies [REQ-RIDE-080] fields { rideId: uuid; link: TripSharingLink; activatedAt: datetime } }
    event EmergencyTriggered { satisfies [REQ-RIDE-081, REQ-RIDE-082] fields { rideId: uuid; riderId: uuid; driverId: uuid; type: EmergencyEventType; location: Location; triggeredAt: datetime } }
    event UnusualStopDetected { satisfies [REQ-RIDE-084] fields { rideId: uuid; location: Location; stoppedSeconds: int; detectedAt: datetime } }
    event SafetyReportFiled { satisfies [REQ-RIDE-085, REQ-RIDE-086] fields { rideId: uuid; reportedBy: uuid; severity: SafetyReportSeverity; description: string; withinWindow: boolean; filedAt: datetime } }
    event DriverNoShowRecorded { satisfies [REQ-RIDE-067] fields { driverId: uuid; rideId: uuid; recordedAt: datetime } }

    // -----------------------------------------------------------------
    // Temporal events
    // -----------------------------------------------------------------

    temporal-event RideOfferDeadlineElapsed :: "Offer auto-retracts after 15 seconds" {
        satisfies [REQ-RIDE-023]
        relative-to RideOffer.offeredAt offset 15s
        guard RideOffer.status = pending
    }

    temporal-event RiderBoardingDeadlineElapsed :: "Rider must board within 5 min of arrival" {
        satisfies [REQ-RIDE-043]
        relative-to DriverArrived offset 5min
        guard Ride.status = driverArrived
    }

    temporal-event DriverArrivalDeadlineElapsed :: "Driver no-show after ETA + 5 min" {
        satisfies [REQ-RIDE-065]
        relative-to (Ride.estimatedDriverArrivalTime + 5min)
        guard Ride.status = driverAssigned
    }

    temporal-event ScheduledRideMatchingDue :: "Begin matching 15 min before scheduled pickup" {
        satisfies [REQ-RIDE-008]
        relative-to (RideRequest.scheduledPickupTime - 15min)
        guard RideRequest.scheduleType = scheduled and RideRequest.status = searching
    }

    temporal-event ScheduledRideReminderDue :: "Remind rider 30 min before scheduled pickup" {
        satisfies [REQ-RIDE-009]
        relative-to (RideRequest.scheduledPickupTime - 30min)
        guard RideRequest.scheduleType = scheduled
    }

    // -----------------------------------------------------------------
    // External events
    // -----------------------------------------------------------------

    external-event PaymentCaptured from Payment
    external-event ETAVarianceDetected from GeolocationRouting

    // -----------------------------------------------------------------
    // Reactions
    // -----------------------------------------------------------------

    reaction startMatchingOnRequest :: "Trigger matching as soon as the request is filed" {
        satisfies [REQ-RIDE-020]
        trigger RideRequested
        guard event.scheduleType = immediate
        effect MatchingService.findAndOfferDriver(rideRequest = resolve(event.requestId))
    }

    reaction startMatchingOnScheduledLeadTime :: "Begin matching scheduled rides 15 minutes early" {
        satisfies [REQ-RIDE-008]
        trigger ScheduledRideMatchingDue
        effect MatchingService.findAndOfferDriver(rideRequest = resolve(event.requestId))
    }

    reaction createRideOnMatch :: "Materialize a Ride when a driver accepts" {
        satisfies [REQ-RIDE-025]
        trigger RideRequestMatched
        effect CreateRide(
            requestId = event.requestId,
            riderId = resolve(event.requestId).riderId,
            driverId = event.driverId,
            pickupLocation = resolve(event.requestId).pickupLocation,
            dropoffLocation = resolve(event.requestId).dropoffLocation,
            committedFare = resolve(event.requestId).committedFare,
            estimatedDriverArrivalTime = GeolocationGateway.estimateDriverArrival(event.driverId, resolve(event.requestId).pickupLocation)
        )
    }

    reaction retractExpiredOffer :: "Retract pending offer at deadline and roll to next driver" {
        satisfies [REQ-RIDE-023]
        trigger RideOfferDeadlineElapsed
        effect HandleOfferTimeout(offerId = event.offerId)
    }

    reaction cancelOnRiderNoShow :: "Cancel ride if rider has not boarded 5 min after driver arrival" {
        satisfies [REQ-RIDE-043]
        trigger RiderBoardingDeadlineElapsed
        effect CancelRideRiderNoShow(rideId = event.rideId, arrivedAt = event.referenceTime, feeAmount = computeNoShowFee())
    }

    reaction cancelOnDriverNoShow :: "Cancel ride and rematch when driver fails to arrive" {
        satisfies [REQ-RIDE-065, REQ-RIDE-067]
        trigger DriverArrivalDeadlineElapsed
        effect CancelRideDriverNoShow(rideId = event.rideId)
    }

    reaction rematchAfterDriverFault :: "Re-enter matching after driver cancellation or no-show" {
        satisfies [REQ-RIDE-067]
        trigger RideCancelledDriverNoShow
        effect RequestRide(/* clone of original request */)
    }

    reaction suspendOnExcessiveDriverCancellations :: "Suspend driver for 30 min after >3 cancellations in 24h" {
        satisfies [REQ-RIDE-064]
        trigger RideCancelledByDriver
        guard DriverManagementGateway.getCancellationCount(event.driverId, 24) > 3
        effect DriverManagementGateway.requestTemporarySuspension(
            driverId = event.driverId,
            duration = Duration(seconds = 1800),
            reason = "Excessive cancellations in 24h"
        )
    }

    reaction suspendOnExcessiveDriverNoShows :: "Suspend driver for 1 hour after >2 no-shows in 24h" {
        satisfies [REQ-RIDE-066]
        trigger RideCancelledDriverNoShow
        guard DriverManagementGateway.getNoShowCount(event.driverId, 24) > 2
        effect DriverManagementGateway.requestTemporarySuspension(
            driverId = event.driverId,
            duration = Duration(seconds = 3600),
            reason = "Excessive no-shows in 24h"
        )
    }

    reaction publishDriverNoShowForReliability :: "Publish DriverNoShowRecorded for Driver Management reliability score" {
        satisfies [REQ-RIDE-067]
        trigger RideCancelledDriverNoShow
        effect publish DriverNoShowRecorded(driverId = event.driverId, rideId = event.rideId, recordedAt = now())
    }

    reaction notifySafetyTeamOnEmergency :: "Notify safety team within 60s of emergency button press" {
        satisfies [REQ-RIDE-082]
        trigger EmergencyTriggered
        effect RideNotificationService.notifySafetyTeam(rideId = event.rideId, eventType = event.type)
    }

    reaction shareWithEmergencyServicesIfConfigured :: "Share location with local emergency services where integrated" {
        satisfies [REQ-RIDE-083]
        trigger EmergencyTriggered
        guard marketSupportsEmergencyIntegration(event.rideId)
        effect RideNotificationService.shareLocationWithEmergencyServices(rideId = event.rideId, location = event.location)
    }

    reaction safetyCheckOnUnusualStop :: "Send safety check ping to rider on unusual stop" {
        satisfies [REQ-RIDE-084]
        trigger UnusualStopDetected
        effect RideNotificationService.sendSafetyCheck(riderId = resolve(event.rideId).riderId, rideId = event.rideId, stop = event.location)
    }

    reaction notifyRiderOnDriverNoShow :: "Tell rider a new driver is being found" {
        satisfies [REQ-RIDE-067]
        trigger RideCancelledDriverNoShow
        effect RideNotificationService.notifyRider(
            riderId = event.riderId,
            message = "Your driver did not arrive — we are finding a new driver"
        )
    }

    // -----------------------------------------------------------------
    // Agreement (cross-aggregate consistency)
    // -----------------------------------------------------------------

    agreement RideRequestRideConsistency :: "RideRequest.matched ↔ Ride exists for that request" {
        satisfies [REQ-RIDE-025]
        participants [RideRequest, Ride]
        reconciliation orphanRideOrUnmaterializedMatch :: "Detect requests in matched status without a Ride" {
            detection "RideRequest.status = matched and not exists Ride where rideRequestId = RideRequest.requestId for > 60s"
            response alert
            escalation [operatorReviewQueue, engineeringOnCall]
        }
    }
}