cat ~/production/vehicle-rental-platform/REPORT.md

Vehicle Rental Platform

Rent a scooter or e-bike from an app: scan the QR, the physical lock opens over cellular, ride, park in a permitted zone, pay by the minute from a prepaid wallet.

role Tech Lead, Backend and Infrastructuretimeline 2025category platformclient A micromobility fleet operator

Problem

A fleet operator needed riders to find a vehicle on a live map, unlock it physically from their phone, and be billed per minute. The hardware had already been bought from an overseas manufacturer, so the system had to speak whatever protocol those locks spoke. Nothing off the shelf covers local payment rails, trilingual UI, or that specific hardware.

Approach

Twelve collections around a User, Ride, Vehicle spine with money isolated in wallet and deposit ledgers. The interesting part is the transport layer. The locks speak a proprietary binary protocol over raw TCP, riders speak HTTP, the operator dashboard speaks WebSocket. I built one pipeline terminating all three against a single domain layer, so a lock checking in over TCP updates vehicle state that fans out to the live map in the same tick. Geofences are GeoJSON polygons evaluated server-side on every telemetry beat.

Outcome

Full ride lifecycle running on real vehicles: unlock, live tracking, parking enforcement, per-minute billing, penalties and refunds. Verified in the field on physical hardware, not only in simulation.

12Domain models
3Protocols · TCP, HTTP, WebSocket in one pipeline
AR / EN / KULocales
YesField tested · real vehicles, real locks
architecture --explain

How it's built

Three protocols, one process. Raw TCP for IoT lock telemetry and commands, REST for the rider app, WebSocket for the operator dashboard, all sharing one domain layer. Vehicle state is the single point of truth every transport reads and writes through.

Compare-and-swap instead of a transaction to claim a vehicle

Two riders scanning the same QR at once is the defining race in this domain. A conditional atomic update makes the guard and the lock the same operation, because the status filter is what prevents the second claim.

No transaction means the physical unlock that follows needs an explicit compensating rollback. Without the status clause in the filter, both requests pass validation and two riders get billed for one scooter.

Compensating rollback when the hardware refuses

The database claim happens before the physical unlock, so a lock that fails to open leaves a vehicle marked in use with nobody riding it. The failure path releases the claim so the vehicle stays rentable.

A narrow window exists where the lock opened but the acknowledgement was lost. Handled operator-side rather than pretending it cannot happen.

Price snapshotting onto the ride at unlock

A ride copies the per-minute rate and unlock fee when it starts and never re-reads them, so changing the tariff cannot retroactively re-price a ride already in progress.

Denormalised pricing on every ride, the right trade when the alternative is billing disputes.

related --by-stack

Adjacent systems

REL-001 / platform

Freelance Marketplace with Escrow

Two-sided marketplace for architecture and design work: the platform holds the money until delivery is confirmed, with bidding happening in real-time chat.

REL-002 / platform

Tourism Discovery Platform

Find hotels, restaurants and attractions near you, ranked by real distance, across three separate venue types in one query.

REL-003 / saas

Multi-tenant Restaurant Platform

A complete restaurant operating system on one multi-tenant backend: customer ordering, driver dispatch, owner dashboard and point of sale.