03Relational SystemsPublic repository · two contributors

RideFlow

A multi-role ride-hailing simulation where the business rules live in MySQL — procedures, triggers and constraints — rather than in the Express layer above them.

Tables
16 · 29 foreign keys
Database logic
2 procedures · 8 triggers · 6 views
API routes
43
Runtime dependencies
Four — no ORM
Context
Academic project · public repository
Contributors
Rafay Khattak & Muhammad Umar Nadeem
Period
May 2026
Shape
One Express service, three role surfaces, one MySQL schema
Where the rules live
In the database — procedures, triggers, views and constraints
Status
Public prototype · the hosted demo no longer responds
Visibility
Public · every count on this page is checkable in source

Covers

Systems Programming
Data
Databases
Interface
Product
People

Stack

  • Express
  • Node.js
  • JavaScript
  • MySQL 8
  • SQL
  • Railway
  • Aiven
01

The domain

A ride-hailing platform is a deceptively good relational problem. Two parties who do not know each other are matched by a third, money moves in one direction and settles in the other, and every step has to leave a record that can be argued about later.

So the project starts from the schema rather than the screens. Riders, drivers, vehicles, locations, fare rules and promotions exist before a ride does; rides produce payments, ratings and complaints; payments feed wallets, and wallets feed payouts. Sixteen tables, joined by twenty-nine foreign keys.

The states are declared rather than implied. A driver is pending, verified or rejected. A payment is pending, paid, failed or refunded. A complaint is open, under review, resolved or rejected. Writing those as enumerated columns means an impossible state is rejected by the store, not merely avoided by the code that happens to be writing at the time.

  1. USERS
  2. FARE RULES
  3. DRIVERS
  4. LOCATIONS
  5. RIDES
  6. VEHICLES
  7. WALLETS
  8. PAYMENTS
  9. RATINGS
  10. PAYOUTS
  11. COMPLAINTS
  12. HISTORY
02

The stack

Above the schema sits a single Express service — 43 routes across roughly 2,500 lines — that handles sessions, checks the caller's role, serves the static front end and talks to MySQL through a connection pool.

It leans on four runtime dependencies and no ORM. Queries are SQL, and the calls that matter invoke stored procedures directly. On a project whose subject is relational design, an ORM would have hidden exactly the thing being demonstrated.

The front end is vanilla JavaScript organised as one shared application shell — API wrapper, auth flow, state, UI helpers, device detection — with per-role feature modules layered on top. Which audience a visitor gets is resolved from the path, so all three dashboards ship from one service rather than three deployments.

    • Rider surface

      Booking

    • Driver surface

      Trips

    • Admin surface

      Oversight

  1. Shared app shell

    Audience by path

  2. Express API

    43 routes

  3. Sessions & role checks

    Per request

  4. Procedures & triggers

    Rules in SQL

  5. MySQL 8

    16 tables · 29 foreign keys

  • Reporting views

    Six, read directly

  • Database roles

    Four, distinct grants

  • Scheduled event

    Nightly promo expiry

03

Rules in the database

This is the decision the project is really about. The business rules are not in the Express layer; they are in MySQL, as procedures, triggers, constraints and views.

Two stored procedures carry the operations that must not be got wrong: calculating a fare, and requesting a ride. Eight triggers hold the invariants around them — validating a driver assignment before a ride row is written or updated, computing the driver's net share before a payment lands, finalising a payment once it is marked paid, recalculating rating averages when a rating arrives, maintaining trip counts, and archiving a ride into history when it reaches a terminal state.

The trigger names are worth a mention on their own: each one states its table, its timing, its operation and its purpose. Reading the list tells you what the database guarantees without opening a single body — which is the difference between logic that lives in the store and logic that merely happens to be stored there.

A scheduled event expires promotional codes overnight, so a promotion ending is a property of the data rather than a job somebody has to remember to run.

  • Stored procedures
  • Row-level triggers
  • Reporting views
  • Scheduled events
  • Foreign-key constraints
  • Enumerated states
04

One ride, end to end

The lifecycle is where the design has to hold together. A rider requests a trip against a fare estimate; a driver is offered it and accepts, or the rejection is recorded so the same offer is not made again; the trip progresses through its live states; a payment settles and the platform's commission is split from the driver's share; both sides rate each other; and the completed ride is archived.

Every one of those transitions has something enforcing it underneath — a constraint, a trigger, or a procedure — rather than an application function that a second caller could bypass.

  1. 0116 tables joined by 29 foreign keys
  2. 02Two stored procedures — fare calculation and ride request
  3. 03Eight triggers covering assignment, payment, rating and archival
  4. 04Six reporting views, plus a nightly promotion-expiry event
  5. 05Four database roles with distinct grants
  6. 06Lifecycle states declared as enumerated columns, not conventions

Known-data KPI test

  1. Requested
  2. Assigned
  3. Accepted
  4. In progress
  5. Completed
  6. Paid
  7. Rated

The chain names the states a ride moves through, read from the schema itself. Nothing here is a performance, throughput or usage claim — the repository shows a design, not a workload.

05

Reporting in SQL

The admin dashboard's numbers are not assembled in JavaScript. Six views define them in SQL: active rides, top drivers, a per-city driver leaderboard, revenue by city and day, revenue by payment method, and refund and dispute totals.

The benefit is that a definition exists once. "Revenue by city" means one thing, written in one place, and the reporting screen cannot quietly disagree with any other question asked of the same data.

Completed rides and their settled payments

City × day, and by payment method

  • Fare collected
  • Platform commission
  • Driver net
  • Refunds and disputes

Six views the admin dashboard reads directly

One definition, in SQL

A view means the meaning of a number is written once. The reporting screen cannot drift from any other question asked of the same rows, because there is no second implementation for it to drift away from.

fare − commission → driver net
06

What that choice costs

Putting the rules in the database is not free, and the project is a fair place to see both sides of it. Constraints and triggers apply to every caller — including a future one written by somebody who never read the API — and they survive an application bug rather than being defeated by it.

The cost is that the system's logic is split across two languages, that a trigger is markedly harder to unit-test than a function, and that debugging a rejected write means crossing a boundary to find out which layer said no.

For a project whose subject is relational design, the trade lands on the right side. For a system expected to change weekly, it would be a harder argument to make.

Rules in the database

  • Applied to every caller
  • Survive an application bug
  • One definition, written in SQL
  • Invalid state rejected at the store

What it costs

  • Logic split across two languages
  • A trigger is hard to unit-test
  • Schema changes are SQL files
  • Debugging crosses a boundary
  • Constraints
  • over
  • Convention
07

Three surfaces

Each role gets its own dashboard rather than one interface with things hidden. A rider books and pays; a driver works and earns; an administrator verifies, prices, adjudicates and reports. The workflows barely overlap, and pretending otherwise would have produced a screen that served none of them well.

Underneath they share a shell, an API wrapper and a state module, so the three surfaces stay consistent without being the same page wearing different navigation.

Rider

  • Booking with fare estimate
  • Wallet and top-ups
  • Ride history
  • Rating and complaints

Driver

  • Incoming ride offers
  • Live trip progression
  • Earnings and wallet activity
  • Payout requests

Administrator

  • Driver and vehicle verification
  • Fare and city rules
  • Complaints and refunds
  • Payout processing

Money

  • Wallet ledger
  • Commission tracking
  • Driver earning credits
  • Payout workflow

Trust & safety

  • Mutual ratings
  • Complaint review
  • Refunds
  • Review flags

Shared shell

  • One API wrapper
  • Shared client state
  • Automatic refresh
  • Path-based audience detection

Three role surfaces served by one Express application. The repository is public, so this list is checkable rather than asserted.

08

Deployment practice

The project was deployed as one Node service on Railway, connected over TLS to managed MySQL at Aiven, with the certificate supplied as configuration rather than committed to the repository.

A bundled initialization script applies the committed SQL — schema, then procedures and triggers, then the bootstrap reference data — against whichever database is configured. That is what makes the project restartable: the database is reproducible from source rather than from a backup somebody has to still be holding.

This is evidence that the project can be connected to hosted infrastructure. It is not a claim of production operations experience, and the hosted demo is currently offline.

  1. Browser

    Three role entry points

  2. Railway

    Single Node service

  3. Express server

    API and static files

  4. Connection pool

    TLS to the provider

  5. Aiven

    Managed database

  6. MySQL 8

    09

    How it grew

    The order followed the dependency, not the demo. The schema and its constraints came before any endpoint existed, the procedures and triggers before the API that calls them, and the dashboards last — because a surface built over rules that are not yet settled has to be rebuilt when they are.

    1. 01

      Schema first

      Sixteen tables and twenty-nine foreign keys, before any endpoint existed.

    2. 02

      Rules in SQL

      Procedures, triggers and constraints, so invalid state is rejected at the store.

    3. 03

      The API

      An Express layer over the procedures, with sessions and role checks.

    4. 04

      Three surfaces

      Rider, driver and admin dashboards over one shared client shell.

    5. 05

      Reporting

      Views for revenue, leaderboards and disputes, read straight by the admin screens.

    6. 06

      Deployment

      A single Node service on Railway against managed MySQL, reproducible from committed SQL.

    10

    What holds, and what does not

    Because the repository is public, the left column below is unusually strong for a portfolio: none of it has to be taken on trust. Anyone can open the SQL and count.

    The right column is the honest other half. There are no automated tests — the committed verification is a file of SQL queries run by hand — nothing has been measured under load or concurrency, the hosted demo no longer answers, and the contribution split between the two authors is not written down anywhere, so it is not asserted here either.

    Verified

    • The schema, procedures, triggers and views — the repository is public
    • The full ride lifecycle, from request through settlement to rating
    • Database-side roles and their differing grants
    • That the database is reproducible from committed SQL

    Not verified

    • Any workload — no load, concurrency or scale testing exists
    • Automated tests — verification is SQL queries run by hand
    • The hosted demo, which no longer responds
    • The split of work between the two contributors

    Next proof

    1. Automated tests
    2. A seeded load run
    3. A restored demo
    11

    Current status

    The repository, its schema, its SQL logic and its entity-relationship diagram are all public and available. The hosted demo is not: all three role entry points return a 404, which I checked rather than assumed.

    The project can still be brought up from a clean start. The committed SQL and the bundled initialization script establish a fresh database, after which a local or hosted instance can be pointed at it.

    Public repository
    Available
    Schema & SQL logic
    Available
    Entity-relationship diagram
    Published in the repository
    Database initialization
    Supported from committed SQL
    Hosted demo
    Offline — returns 404
    Automated tests
    None committed

    Evidence boundary

    Supported

    The repository is public, so everything on this page can be checked directly: 16 tables joined by 29 foreign keys, two stored procedures, eight triggers, six reporting views, a scheduled event, four database roles with distinct grants, and 43 Express routes serving three role surfaces.

    Not overstated

    The hosted demo no longer responds. There are no automated tests — verification is a set of SQL queries run by hand — and no load, concurrency or scale testing exists anywhere in the project. The split of work between Rafay Khattak and Muhammad Umar Nadeem is not documented, so no division of credit is claimed here.

    Technical notes

    The project runs as a single Express service that serves three role surfaces and talks to MySQL 8 over TLS. Fare calculation, driver assignment, commission splits, rating averages and ride archival are stored procedures and triggers rather than application code. It was previously deployed on Railway against managed MySQL; that demo no longer responds, and the bundled initialization script can still bring up a fresh database from the committed SQL.

    Core stack

    • Express
    • Node.js
    • JavaScript
    • MySQL 8

    Database logic

    • Stored procedures
    • Triggers
    • Reporting views
    • Scheduled event
    • Roles and grants

    Front end

    • Vanilla JavaScript modules
    • One shared shell
    • Path-based audience detection

    Deployment & services

    • Railway
    • Aiven
    • TLS to the database
    • Scripted initialization
    Repository
    Public
    Public artifacts
    The repository publishes its entity-relationship diagram. Screenshots of the rider, driver and admin surfaces can be added here once captured.

    Contributors

    • Rafay Khattak
    • Muhammad Umar Nadeem