Overview
Rydvu is a US ride-hailing platform: riders request tiered vehicles, drivers go online and complete trips, operators manage surge, zones, and documents, and money moves through Stripe with weekly driver payouts.
This case study focuses on the production system shape — NestJS microservices, Expo mobile clients, realtime location, and payment ownership — and on John Prakash Balireddy’s primary contribution: the driver mobile client, with supporting backend and web work. Claims about John’s scope are grounded in authorship patterns across the driver app and related services; they are not a claim of sole ownership of payments or the entire backend.
The narrative below is complete without screenshots. Gallery entries describe product surfaces in text.
Business problem
Ride-hailing only works when several hard problems stay correct at once:
- Matching — connect a rider request to a nearby, eligible driver for the right vehicle tier and service area.
- Live operations — drivers must appear online with usable GPS; trips must survive app restarts and flaky networks.
- Money — fares, cancellations, tips, and payouts must settle correctly under failure (webhooks, retries, idempotency).
- Trust & compliance — driver KYC/documents, PIN at pickup, SOS/safety paths, and operator tooling for oversight.
- Upfront clarity — riders expect transparent pricing and tracking; drivers expect earnings and bank payouts they can rely on.
Rydvu targets industrial / Uber–Lyft-class flows for markets such as CT, NY, NJ, and TX, with vehicle tiers including Economy, Comfort, XL, Luxury, and Moto.
Solution
Rydvu splits the product into dedicated surfaces and owned services:
- Driver mobile (Expo) — go online, background location, matching UX, trip lifecycle, onboarding/KYC capture.
- Rider mobile (Expo) — earlier-stage client on the same stack.
- Rider & admin web (Next.js) — booking/tracking and operations.
- Backend (NestJS monorepo) — domain services behind an API gateway, events over RabbitMQ, realtime over a dedicated WebSocket gateway.
- Payments — Stripe PaymentIntents, Connect payouts, and tip handling owned exclusively by payment-service (trip-service never calls Stripe).
Trip happy path (simplified): request → matching (H3 + tier fallbacks) → accept → arrive / PIN → in progress → complete → trip.completed → Stripe charge → payment.completed → driver earnings → weekly Connect payouts.
Architecture
Mobile / Web clients
│ REST
▼
api-gateway (Fastify)
│
├── auth · rider · driver · trip · matching · pricing · …
│
└── (events) RabbitMQ topic exchange
├── payment-service → Stripe
├── notification-service → FCM / SMS / email
└── …
Clients (WS) → realtime-gateway → location / trip HTTP backends
Verified structural choices:
| Concern | Approach | |---|---| | Service data | One Postgres database per service (Prisma); no shared cross-service DB | | Messaging | RabbitMQ event bus (not Kafka in the current architecture) | | Realtime | Dedicated realtime-gateway; Redis-backed Socket.IO; REST kept separate | | Location | location-service + driver background GPS; coarse GPS accepted for discoverability | | Payments | payment-service owns all Stripe; dual platform/Connect webhooks | | Config | RuntimeConfig keys tunable without redeploy | | Deploy | AWS EKS, Helm, ArgoCD, ECR; OpenTelemetry / Prometheus / Grafana |
John’s focus in this architecture: the driver client talking to auth, matching, trip, and location/realtime paths — including recovery after disconnect — plus smaller backend/web fixes where the client required them (for example location JWT/WS concerns, registration device identity, document-review related bumps).
Challenges
Realtime presence without stranding drivers
Drivers background the app. Networks drop. The product cannot strand an active trip or a pending assignment. The driver client implements active-trip recovery: restore active trip, else pending assignment, else idle — with reconnect/resubscribe behavior on trip sockets.
GPS that is “good enough” for matching
Strict GPS filters can hide drivers who are physically nearby. The system accepts coarse GPS in defined cases so matching still sees drivers, while still streaming updates on a practical interval (documented around ≥8s class cadence) with speed handled in mph for the product’s market.
Money correctness under failure
Fares, cancel fees, tips, and payouts fail in the real world (webhooks delayed, intents stuck, Connect KYC incomplete). Architecture isolates Stripe inside payment-service, uses idempotent earnings keyed by trip, treats tips as 100% driver (platform absorbs Stripe tip processing cost), and seals cancel settlement decisions before payment executes VOID / fee / partial capture paths.
Client/backend contract drift
Mobile and many services evolve in parallel. Typed TypeScript clients, TanStack Query, and explicit REST/WS boundaries reduce — but do not eliminate — drift. Matching UX on the driver app historically relied on short-interval polling (documented ~3s) where product requirements demanded it.
Technical decisions
- Microservice ownership over a shared DB — each domain keeps its own Postgres schema so trip, payment, and driver data do not become a distributed monolith by accident.
- RabbitMQ instead of Kafka (current) — operational simplicity for this stage; events fan out to payment, notify, matching, and related consumers.
- Realtime isolated from REST — WebSocket traffic hits realtime-gateway so trip/location fan-out does not share fate with the API gateway’s request path.
- Stripe only in payment-service — trip completion emits events; payment executes PaymentIntents; earnings are idempotent on
tripId. - Tips never subsidize platform commission — drivers receive 100% of tips; processing fees are a platform expense.
- Connect payouts with platform-collected KYC — Connect Accounts configured for platform dashboard collection; Financial Connections for bank verify; weekly payout schedule with debt/block rules.
- Expo + feature-oriented React Native — driver app structured by features (location, trips, onboarding, maps) with Zustand, TanStack Query, and Axios.
- Honesty about scope — payments ledger/settlement depth and full rider-app productization are platform efforts; this case study does not claim John as sole author of those subsystems.
Responsibilities
Primary (driver client — inferred from sustained authorship on the driver app):
- Maps, tile/proxy-facing UX, and on-duty background location
- Matching accept/decline UX and trip lifecycle presentation
- Active-trip recovery and websocket reconnect behavior
- Onboarding / registration and KYC document metadata capture
- Session/auth client paths and related product polish (chat, analytics entry points)
Supporting:
- Backend fixes touching location/authz, registration device identity, and document-review related paths
- Smaller rider/web contributions (for example auth/session refactors, PIN-related web flow)
Gallery
No marketing screenshots are required to understand the product. These are the surfaces that matter:
- Go online & matching — Driver presence, nearby matching signals, and accept/decline against live assignment state.
- Active trip & recovery — In-trip experience with restore paths after backgrounding or process death.
- Location & maps — Background location while on duty; map presentation tuned for discoverability.
- Onboarding & documents — License and vehicle document capture before a driver can operate.
(Optional image assets can be added later without changing this narrative.)
Technologies
Mobile: React Native, Expo, Expo Router, TypeScript, Zustand, TanStack Query, Axios, Socket.IO client, expo-location (background).
Backend: NestJS, TypeScript, Prisma, PostgreSQL (per service), RabbitMQ, Redis, BullMQ, Fastify API gateway, dedicated realtime gateway.
Payments: Stripe PaymentIntents, SetupIntent, Connect, Financial Connections, dual webhooks.
Platform: AWS EKS, Helm, ArgoCD, ECR, OpenTelemetry, Prometheus, Grafana.
Web (platform): Next.js admin/rider/public/connect apps in the broader Rydvu surface area.
Lessons learned
- Recovery is a feature. Trip restore and socket resubscribe are product requirements, not afterthoughts.
- Money needs a single owner. Letting trip-service call Stripe would entangle domain events with processor failure modes.
- Matching UX is physics plus policy. H3 areas, tiers, GPS quality, and polling/realtime choices all show up as driver trust.
- Document what you did not build alone. Case studies that over-claim ownership fail the first technical interview.
Future improvements
- Deepen rider mobile product parity with the driver client’s production maturity.
- Continue settlement/ledger hardening and payment-recovery accounting on the platform side.
- Replace remaining polling with stronger push/realtime where product and infra allow.
- Complete push notification client wiring where backend FCM paths already exist.
- Publish optional visual gallery assets once approved for public portfolio use.
- Keep engineering essays current as payment, matching, and client recovery patterns evolve.