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: Decimallongitude: Decimal
Route
Local projection of GEO’s Route — waypoints, distance, duration.
- Fields:
waypoints:list<Location>distance:Distanceduration:Duration
FareEstimate
Committed fare shown at confirm — the upfront-fare promise.
- Fields:
amount:AmountsurgeMultiplier: Decimal —min(1.0)distance:Distanceduration:DurationcommittedAt: 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:AmountsurgeMultiplier: Decimal —min(1.0)actualDistance:DistanceactualDuration:DurationcomputedAt: DateTime
- Realizes: REQ-RIDE-048
CancellationDetail
Reason, party, fee outcome, and timestamp for any cancellation — the audit-trail record.
- Fields:
reason:CancellationReasoncancelledBy: StringcancelledAt: DateTimefeeCharged: BooleanfeeAmount:Amountoptional
- Realizes: REQ-RIDE-060, REQ-RIDE-061, REQ-RIDE-062, REQ-RIDE-NFR-005
TripSharingLink
Short-lived live-tracking link delivered to a rider’s emergency contact.
- Fields:
linkToken: StringcontactName: StringcontactPhone:PhoneNumberactivatedAt: DateTimeexpiresAt: DateTime
- Realizes: REQ-RIDE-080
PhoneNumber
- Fields:
countryCode: Stringnumber: String
Entities
RideOffer
Offer of a ride to a single driver — short-lived, deadline-bounded.
- Identifier:
offerId : UUID - Fields:
rideRequestId: UUIDdriverId: UUIDstatus:RideOfferStatus— defaultpendingofferedAt: DateTimerespondedAt: DateTime optionaltimeoutSeconds: Integer — default15
- 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: UUIDtype:EmergencyEventTypetriggeredBy: UUIDtriggeredAt: DateTimelocation:LocationacknowledgedAt: DateTime optionalresolvedAt: 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: UUIDreportedBy: UUIDseverity:SafetyReportSeveritydescription: String —maxLength(4000)reportedAt: DateTimewithinWindow: Booleanescalated: Boolean — defaultfalseescalatedAt: 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: UUIDstatus:RideRequestStatus— defaultsearchingscheduleType:RideScheduleType— defaultimmediatescheduledPickupTime: DateTime optionalpickupLocation:LocationdropoffLocation:LocationcommittedFare:FareEstimatematchedDriverId: UUID optionalmatchedAt: DateTime optionalcancellation:CancellationDetailoptionalcreatedAt: DateTimeupdatedAt: DateTime
- Invariants:
scheduledHasFutureTime: whenscheduleType = scheduled,scheduledPickupTime > now()— enforcement: rejectmatchedHasDriver: whenstatus = matched,matchedDriverIdis defined — enforcement: rejectcancelledHasReason: whenstatus = cancelled,cancellationis defined — enforcement: reject
- Operations:
Request a rideon commandRequestRide- Realizes: REQ-RIDE-001, REQ-RIDE-002, REQ-RIDE-003, REQ-RIDE-004, REQ-RIDE-005
- Preconditions:
riderReadyToRide:requestRide.riderReadyToRide = truecommittedFareProvided:requestRide.committedFare is definedpaymentMethodValid:requestRide.paymentMethodValid = true
- State changes:
riderId,pickupLocation,dropoffLocation,committedFare,scheduleType,scheduledPickupTime,status = searching,createdAt = now(),updatedAt = now(). - Emits:
RideRequestedwithrequestId,riderId,pickupLocation,dropoffLocation,committedFare,scheduleType,requestedAt.
Match driveron commandAcceptRideOffer- Realizes: REQ-RIDE-021, REQ-RIDE-025
- Precondition
stillSearching:RideRequest.status = searching. - State changes:
status = matched,matchedDriverId = acceptRideOffer.driverId,matchedAt = now(). - Emits:
RideRequestMatchedwithrequestId,driverId,matchedAt.
Cancel request before assignmenton commandCancelRequestByRider- Realizes: REQ-RIDE-006, REQ-RIDE-060
- Precondition
stillSearching:RideRequest.status = searching. - State change:
status = cancelled;cancellation = { reason: riderBeforeAssignment, cancelledBy: "rider", feeCharged: false }. - Emits:
RideRequestCancelledByRiderwithrequestId,riderId,cancelledAt.
Cancel request — no driver availableon commandCancelRequestNoDriver- Realizes: REQ-RIDE-024
- Precondition
searchExhausted:cancelRequestNoDriver.searchExhausted = true. - State change:
status = cancelled;cancellation = { reason: noDriverAvailable, cancelledBy: "system", feeCharged: false }. - Emits:
NoDriverAvailablewithrequestId,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: UUIDriderId: UUIDdriverId: UUIDstatus:RideStatus— defaultdriverAssignedpickupLocation:LocationdropoffLocation:LocationcommittedFare:FareEstimateestimatedDriverArrivalTime: DateTimeactualPickupLocation:LocationoptionalactualPickupTime: DateTime optionalactualDropoffLocation:LocationoptionalactualDropoffTime: DateTime optionalfinalFare:FinalFareoptionalcurrentRoute:Routeoptionalcancellation:CancellationDetailoptionaltripSharingLinks:list<TripSharingLink>createdAt: DateTimeupdatedAt: DateTime
- Invariants:
completedHasActuals: whenstatus = completed,actualPickupTime,actualDropoffTime, andfinalFareare defined — enforcement: rejectcancelledHasReason: whenstatus = cancelled,cancellationis defined — enforcement: reject
- Operations:
Create ride from matched requeston commandCreateRide- 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 pickupon commandSignalDriverArrival- Realizes: REQ-RIDE-042
- Preconditions:
isAssigned:Ride.status = driverAssignednear100m:signalDriverArrival.distance.meters <= 100
- State change:
status = driverArrived. - Emits:
DriverArrived.
Start rideon commandStartRide- Realizes: REQ-RIDE-044
- Precondition
driverArrived:Ride.status = driverArrived. - State changes:
status = inProgress,actualPickupLocation = startRide.pickupLocation,actualPickupTime = now(). - Emits:
RideStarted.
Update route during tripon commandUpdateRideTracking- Realizes: REQ-RIDE-045, REQ-RIDE-046
- Precondition
inProgress:Ride.status = inProgress. - State change:
currentRoute = updateRideTracking.route. - Emits:
RideLocationUpdated.
Complete rideon commandCompleteRide- 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:
RideCompletedwith actual distance and duration.
Rider cancels after assignmenton commandCancelRideByRider- 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 assignmenton commandCancelRideByDriver- 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-showon commandCancelRideRiderNoShow- Realizes: REQ-RIDE-043
- Preconditions:
driverArrived:Ride.status = driverArrivedfiveMinutesElapsedSinceArrival:now() - cancelRideRiderNoShow.arrivedAt > 5min
- State change:
status = cancelled;cancellation = { reason: riderNoShow, cancelledBy: "system", feeCharged: true, feeAmount }. - Emits:
RideCancelledRiderNoShow.
Cancel due to driver no-showon commandCancelRideDriverNoShow- Realizes: REQ-RIDE-065, REQ-RIDE-067
- Preconditions:
isAssigned:Ride.status = driverAssignedpastEtaPlusFive:now() > Ride.estimatedDriverArrivalTime + 5min
- State change:
status = cancelled;cancellation = { reason: driverNoShow, cancelledBy: "system", feeCharged: false }. - Emits:
RideCancelledDriverNoShow.
Activate trip sharingon commandActivateTripSharing- Realizes: REQ-RIDE-080
- Precondition
isLive:Ride.statusis one ofdriverAssigned,driverArrived,inProgress. - State change: append the new link to
Ride.tripSharingLinks. - Emits:
TripSharingActivated.
Trigger emergencyon commandTriggerEmergency- Realizes: REQ-RIDE-081, REQ-RIDE-082, REQ-RIDE-NFR-004
- Emits:
EmergencyTriggeredwithtype,location,triggeredAt.
Detect unusual stopon commandDetectUnusualStop- Realizes: REQ-RIDE-084
- Preconditions:
inProgress:Ride.status = inProgressstoppedFiveMinutesOffRoute:detectUnusualStop.stoppedSeconds > 300 and detectUnusualStop.offRoute = true
- Emits:
UnusualStopDetected.
File post-ride safety reporton commandFileSafetyReport- Realizes: REQ-RIDE-085, REQ-RIDE-086
- Precondition
rideCompleted:Ride.status = completed. - Emits:
SafetyReportFiledwithwithinWindow = (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 invariantmatched— invarianthasDriver:RideRequest.matchedDriverId is definedcancelled— invarianthasCancellation:RideRequest.cancellation is defined
- Transitions:
searching→matchedonAcceptRideOffersearching→cancelledonCancelRequestByRidersearching→cancelledonCancelRequestNoDriver
- Final states:
matched,cancelled
RideLifecycle on Ride
- Start state:
driverAssigned - States:
driverAssigned— no scoped invariantdriverArrived— no scoped invariantinProgress— invarianthasPickupTime:Ride.actualPickupTime is definedcompleted— invarianthasFinalFare:Ride.finalFare is definedcancelled— invarianthasCancellation:Ride.cancellation is defined
- Transitions:
driverAssigned→driverArrivedonSignalDriverArrivaldriverArrived→inProgressonStartRideinProgress→completedonCompleteRidedriverAssigned→cancelledonCancelRideByRiderdriverAssigned→cancelledonCancelRideByDriverdriverAssigned→cancelledonCancelRideDriverNoShowdriverArrived→cancelledonCancelRideByRiderdriverArrived→cancelledonCancelRideRiderNoShow
- Final states:
completed,cancelled
Domain Services
MatchingService
Match a request to the closest eligible driver — proximity, offer rotation, timeout handling.
- Operations:
findAndOfferDriver(rideRequest: RideRequest) : voidhandleOfferTimeout(offerId: uuid) : voidgetDriverAcceptanceRate(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) : datetimegetCurrentLocation(rideId: uuid) : LocationgetCurrentRoute(rideId: uuid) : RouteisStopOffRoute(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) : FareEstimatecomputeFinalFare(rideRequestId: uuid, actualDistance: decimal, actualDuration: int) : FinalFarepreAuthorize(riderId: uuid, fare: FareEstimate) : voidcapturePayment(rideId: uuid) : voidreleaseHold(rideRequestId: uuid) : voidchargeCancellationFee(riderId: uuid, rideRequestId: uuid) : decimalchargeNoShowFee(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) : voidgetCancellationCount(driverId: uuid, windowHours: int) : intgetNoShowCount(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) : voidnotifyDriver(driverId: uuid, message: string) : voidsendTrackingLink(contacts: list<PhoneNumber>, rideId: uuid) : voidnotifySafetyTeam(rideId: uuid, eventType: string) : voidshareLocationWithEmergencyServices(rideId: uuid, location: Location) : voidsendSafetyCheck(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
CreateRidepopulated with the request fields andestimatedDriverArrivalTimecomputed fromGeolocationGateway.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
RequestRidecloning 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
DriverNoShowRecordedto 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 inmatchedstatus without aRide.”- Detection:
RideRequest.status = matched and not exists Ride where rideRequestId = RideRequest.requestId for > 60s - Response: alert
- Escalation:
operatorReviewQueue,engineeringOnCall
- Detection:
- 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]
}
}
}