RideNow is a worked example that applies the full Specy three-layer discipline to a realistic Uber-like ride-hailing platform. The product is specified across three abstraction layers — a Product Requirements Document that names personas, jobs, features, and goals; a set of System Requirements in EARS that name testable obligations; and a Domain Model that names the entities, operations, events, invariants, agreements, and reactions the system realizes. The platform is decomposed into five bounded contexts — Rider Management, Driver Management, Ride Management, Geolocation & Routing, and Payment — each with its own ubiquitous language, its own systems, and its own slice of the three layers. A sixth requirement set (Platform NFR) covers the cross-cutting non-functional obligations that bind the whole platform.
The five bounded contexts
RideNow decomposes into five bounded contexts, each with a short identifier used as the requirement-ID prefix and in the domain DSL.
| Context | Shortname | Responsibility |
|---|---|---|
Rider Management (RDR) |
RDR | Rider registration and phone verification, profile and saved addresses, payment-method references, ratings received, bans and appeals — including a permanentlyBanned final state and the cross-context bridge that records driver-given ratings on RideCompleted. |
Driver Management (DRV) |
DRV | Driver onboarding, identity and document verification, vehicle registration with inspection eligibility, availability state, ratings received, suspension with appeal path. |
Ride Management (RIDE) |
RIDE | Ride lifecycle from request to completion — upfront fare display, driver matching with offer rotation, pickup-to-completion tracking, cancellation policy with rider/driver penalties, and safety tooling (emergency events, trip sharing, post-ride reports). |
Geolocation & Routing (GEO) |
GEO | Real-time driver positioning, proximity queries ordered by ETA to pickup, ETA computation with periodic destination-ETA refresh, ETA variance alerts, dynamic route recalculation, surge-zone detection with a 5.0x multiplier cap and surge-frozen-at-confirmation guarantee. |
Payment (PAY) |
PAY | Payment-method validation, fare calculation, pre-authorization and capture, refunds (system-cancel, driver-fault), driver weekly payout and on-demand instant payout, receipts. |
Shared Kernel (Shared) |
Shared | Cross-cutting value types co-owned by every context: Amount (money + currency), Currency (ISO 4217 reference data — code, numeric code, fraction digits — modeled on java.util.Currency), Distance (meters), Duration (seconds), and the Currencies enum of ISO 4217 codes. Single source of truth for these unit-bearing types — replaces the prior per-context Money and the duplicated local Distance / Duration declarations. |
Each context has its own three pages in the nav: a PRD (who it serves, what problem it solves, what features it offers), a System Requirements page (testable EARS obligations including a per-context Non-Functional set), and a Domain Model page (entities, value types, commands, events, invariants, services, reactions — every element rendered as its own per-element narrative subsection alongside the verbatim .domain DSL).
Totals across the example: ~161 context-scoped EARS requirements plus an organization-level Platform NFR set of 18 — ~179 system requirements in total. The breakdown:
| Set | Reqs | Must | Should | Could |
|---|---|---|---|---|
| Rider Management | 22 | 13 | 9 | 0 |
| Driver Management | 32 | 28 | 4 | 0 |
| Ride Management | 48 | 40 | 5 | 3 (scheduled rides) |
| Geolocation & Routing | 19 | 16 | 3 | 0 |
| Payment | 40 | 34 | 3 | 3 (instant payout) |
| Platform NFR (organization) | 18 | 18 | 0 | 0 |
| Total | 179 | 149 | 24 | 6 |
Cross-context dependencies
The five contexts do not live in isolation — a ride produced by Ride Management needs Geolocation to find a nearby driver, Payment to capture the fare, Driver Management to confirm the matched driver is verified and online, and Rider Management to confirm the requester is ready-to-ride. These are asynchronous collaborations across context boundaries, expressed at runtime as events and commands crossing the line, and at the model level as context-map patterns.
| Requirement | Depends on | Nature of the dependency |
|---|---|---|
REQ-RIDE-002 (Ready-to-ride gate) |
Rider Management | Ride ← Rider: gate check on rider status before request |
REQ-RIDE-003 (Upfront fare display) |
Payment | Ride → Payment: compute fare estimate (with surge if any) |
REQ-RIDE-020 (Nearby driver query) |
Geolocation | Ride → Geo: proximity query ordered by ETA to pickup |
REQ-RIDE-045 (Tracking during trip) |
Geolocation | Ride → Geo: consume live location stream and refreshed ETA |
REQ-RIDE-048 / REQ-RIDE-049 (Fare finalization & capture) |
Payment | Ride → Payment: compute final fare and capture authorized amount |
REQ-RIDE-060 (Cancel before assignment) |
Payment | Ride → Payment: release pre-auth on system / pre-assignment cancel |
REQ-RIDE-064 / 066 (Driver penalties) |
Driver Management | Ride → Driver: trigger suspension on excessive cancels or no-shows |
REQ-RDR-006 (Saved payment method) |
Payment | Rider → Payment: validate method reference on save |
REQ-DRV-032 (Match-eligibility) |
Ride / Geo | Driver → Ride/Geo: publish availability so matching can see it |
REQ-GEO-003 (Stale position ineligibility) |
Driver Management | Geo → Driver: notify on ineligibility (DriverPositionStale) |
REQ-GEO-013 / 014 / 015 (ETA refresh, variance, recalc) |
Ride Management | Geo → Ride: in-flight ETA + route updates |
REQ-GEO-023 (Surge visible at fare estimate) |
Ride / Payment | Geo → Ride → Payment: surface multiplier before commit |
REQ-GEO-024 (Surge frozen at confirmation) |
Ride Management | Geo ↔ Ride: do not apply surge after the fare is committed |
REQ-PAY-020 (Pre-authorization) |
Ride Management | Payment ← Ride: hold funds at request time |
REQ-PAY-040 (Driver earnings) |
Ride Management | Payment ← Ride: credit balance on ride completion |
REQ-PAY-060 (Auto refund on system cancel) |
Ride Management | Payment ← Ride: refund on system-driven cancellation |
REQ-PAY-064 (Driver-fault refund) |
Driver Management | Payment → Driver: deduct from driver balance |
These pairs are good places to look at the Domain Model pages for each context — the context-map pattern (Open Host Service, Anti-Corruption Layer, Customer/Supplier, Published Language) is declared on the consuming side, and the published events are listed under each Domain page’s External-bound events section.
How to read this example
Two reading paths work, depending on what you want to learn:
- Follow a context end-to-end. Pick one context (Payment is the most intricate; Geolocation & Routing is the most query-heavy and the easiest to skim) and read its three pages in order: PRD → System Requirements → Domain Model. You’ll see the same concepts —
Payment,FareBreakdown,Capture,SurgeMultiplier,RideRequest— surfaced with different grain at each layer, using the same vocabulary at every step. - Follow a layer across contexts. Pick one layer (the PRD group is the most approachable) and read across all five contexts. You’ll see how bounded contexts carve the product into manageable pieces without breaking the ubiquitous language.
For a first pass, the best sequence is: this overview → one PRD → the matching SysReq → the matching Domain Model. Start with Rider Management if you want the simplest context (single aggregate root with a clean lifecycle); start with Geolocation & Routing if you want the smallest domain with the clearest cross-context surface; start with Payment if you want the deepest model.
What this example illustrates
scoped-todiscipline. Every system requirement isscoped-toexactly one bounded context. A single product feature — e.g., “Free cancellation before driver assignment” — typically produces multiple requirements, one per context involved. See howREQ-RIDE-060(ride cancellation) andREQ-PAY-060(system-cancel refund) realize one PRD feature across two contexts, with the Payment side reacting to events emitted by the Ride side.satisfiestraceability. Every domain element that realizes a requirement declaressatisfies [REQ-…]. Coverage queries traverse these lists; orphans (emptysatisfies) are flagged for review. Each Domain page closes with a Traceability Matrix table mapping every requirement in scope to the elements that realize it.- Ubiquitous language sharing. The same noun means the same thing across all three layers within a context.
RideRequestappears in the Ride Management PRD (as a user-visible action), in the SysReq (as the subject of the obligation), and in the Domain Model (as an entity with a state machine). No translation table is needed to follow a concept across layers. When a noun must mean different things in different contexts (e.g., driver in Driver Management’s verification lifecycle vs. driver in Ride Management’s offer/dispatch flow), the metamodel forces an explicit cross-context translation. - Context-map patterns in practice. Ride Management consumes nearby-driver queries from Geolocation through an Open Host Service. Geolocation publishes
SurgeActivated/SurgeDeactivated/ETAVarianceDetectedto Ride Management as a Published Language. Driver Management consumesDriverPositionStalefrom Geolocation through an Anti-Corruption Layer that translates it into a localGoOffline(reason=stalePosition)command. Payment cross-credits/debits Driver Management balances on capture and refund. Each pattern is visible on the Context Map table at the top of each Domain page. - Verifiable properties. Every aggregate carries invariants (with explicit enforcement: rejection, compensation, or alert). Every operation carries preconditions and postconditions. Every state machine carries transition guards and state-scoped invariants. Cross-aggregate truths use agreements maintained by reconciliations with bounded inconsistency windows. Together they define the property-based test surface — the obligations a generator can drive into the system to falsify. This is the built-in verifiability the Specy domain metamodel guarantees.
- Temporal events and reactions. Several lifecycle behaviors are driven by temporal events (e.g.,
RideOfferDeadlineElapsed, weekly payout cutoffs) and reactions that fire on internal or external events. Reactions name the cross-context behavior explicitly rather than hiding it in implementation code.
Pick a context from the nav on the left to begin.