Skip to main content

Design a Ride-Sharing/Booking system

Problem Statement

Design a ride-sharing system where users can request rides from one location to another. The system should match users with nearby drivers, allow drivers to accept rides, and complete trips while tracking ride status.


Functional Requirements

  • Users should be able to request a ride from source to destination
  • System should match the ride with an available nearby driver
  • Driver should be able to accept or reject a ride request
  • Ride status should be tracked (REQUESTED, ACCEPTED, COMPLETED, CANCELLED)
  • Once a ride is completed, driver becomes available again

Objects Required

  • User
  • Driver
  • Location
  • Ride
  • RideStatus
  • RideService
  • DriverMatchingStrategy

RideStatus Enum


public enum RideStatus {
    REQUESTED,
    ACCEPTED,
    COMPLETED,
    CANCELLED
}

This enum represents the lifecycle of a ride. Every ride starts in a requested state and moves through different stages depending on driver and system actions.


Location Class


public class Location {

    double x;
    double y;

    public Location(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double distanceFrom(Location other) {
        return Math.sqrt(
            Math.pow(this.x - other.x, 2) +
            Math.pow(this.y - other.y, 2)
        );
    }
}

The constructor simply stores the coordinates of a point in the city.

The distanceFrom() method calculates the distance between two locations. This helps in finding the nearest driver during ride matching.


User Class


public class User {

    private String userId;
    private String name;
    private Location location;

    public User(String userId, String name, Location location) {
        this.userId = userId;
        this.name = name;
        this.location = location;
    }

    public Location getLocation() {
        return location;
    }

    public String getName() {
        return name;
    }
}

The constructor initializes a user with identity details and current location.

The getLocation() method is used by the system to determine where the user is requesting the ride from.

The getName() method is used for display and logging purposes.


Driver Class


public class Driver {

    private String driverId;
    private String name;
    private Location location;
    private boolean available;

    public Driver(String driverId, String name, Location location) {
        this.driverId = driverId;
        this.name = name;
        this.location = location;
        this.available = true;
    }

    public boolean isAvailable() {
        return available;
    }

    public void setAvailable(boolean available) {
        this.available = available;
    }

    public Location getLocation() {
        return location;
    }
}

The constructor initializes a driver and marks them available by default.

The isAvailable() method helps the system decide whether this driver can be assigned a ride.

The setAvailable() method updates driver status when a ride is accepted or completed.

The getLocation() method is used for matching the closest driver to a user.


Ride Class


public class Ride {

    private String rideId;
    private User user;
    private Driver driver;
    private Location source;
    private Location destination;
    private RideStatus status;

    public Ride(String rideId, User user, Location source, Location destination) {
        this.rideId = rideId;
        this.user = user;
        this.source = source;
        this.destination = destination;
        this.status = RideStatus.REQUESTED;
    }

    public void assignDriver(Driver driver) {
        this.driver = driver;
        this.status = RideStatus.ACCEPTED;
        driver.setAvailable(false);
    }

    public void completeRide() {
        this.status = RideStatus.COMPLETED;
        driver.setAvailable(true);
    }

    public RideStatus getStatus() {
        return status;
    }
}

The constructor creates a ride request and sets its initial status as REQUESTED.

The assignDriver() method connects a driver to the ride and marks the ride as accepted. It also immediately marks the driver as unavailable so they don’t get assigned another ride.

The completeRide() method marks the ride as finished and frees the driver so they can accept new requests again.

The getStatus() method helps external systems track the current state of the ride.


DriverMatchingStrategy Interface


import java.util.*;

public interface DriverMatchingStrategy {

    Driver findDriver(List drivers, Location source);
}

This interface defines the contract for matching drivers. Different strategies like nearest-driver-first or rating-based matching can be implemented later without changing core logic.


NearestDriverMatchingStrategy


import java.util.*;

public class NearestDriverMatchingStrategy implements DriverMatchingStrategy {

    public Driver findDriver(List drivers, Location source) {

        Driver bestDriver = null;
        double minDistance = Double.MAX_VALUE;

        for (Driver driver : drivers) {

            if (!driver.isAvailable()) continue;

            double dist = driver.getLocation().distanceFrom(source);

            if (dist < minDistance) {
                minDistance = dist;
                bestDriver = driver;
            }
        }

        return bestDriver;
    }
}

The method loops through all drivers and ignores those who are not available.

It calculates distance from the user’s location and picks the closest driver.

This keeps matching logic flexible and replaceable in the future.


RideService Class


import java.util.*;

public class RideService {

    private List drivers;
    private DriverMatchingStrategy strategy;

    public RideService(List drivers,
                       DriverMatchingStrategy strategy) {
        this.drivers = drivers;
        this.strategy = strategy;
    }

    public Ride requestRide(User user,
                            Location source,
                            Location destination) {

        Ride ride = new Ride(
                UUID.randomUUID().toString(),
                user,
                source,
                destination
        );

        Driver driver = strategy.findDriver(drivers, source);

        if (driver == null) {
            System.out.println("No driver available");
            return null;
        }

        ride.assignDriver(driver);
        return ride;
    }

    public void completeRide(Ride ride) {
        ride.completeRide();
    }
}

The constructor initializes the system with a pool of drivers and a matching strategy.

The requestRide() method creates a new ride request and asks the strategy to find the best available driver. If no driver is found, the request fails gracefully.

If a driver is found, the ride is assigned immediately and marked as active.

The completeRide() method simply delegates completion logic to the Ride class.


Main Class


import java.util.*;

public class Main {

    public static void main(String[] args) {

        User user =
            new User("U1", "Prasanna", new Location(10, 10));

        Driver d1 =
            new Driver("D1", "Driver A", new Location(11, 10));

        Driver d2 =
            new Driver("D2", "Driver B", new Location(20, 20));

        List drivers =
            Arrays.asList(d1, d2);

        RideService service =
            new RideService(
                drivers,
                new NearestDriverMatchingStrategy()
            );

        Ride ride =
            service.requestRide(
                user,
                new Location(10, 10),
                new Location(15, 15)
            );

        if (ride != null) {
            System.out.println("Ride started successfully");
        }

        service.completeRide(ride);

        System.out.println("Ride completed");
    }
}

The main method creates users and drivers, then initializes the ride service.

A ride request is made, and the system automatically assigns the nearest available driver.

Finally, the ride is completed and the driver becomes available again.


Class Diagram

UseruserId : Stringname : Stringlocation : LocationgetLocation() : LocationgetName() : StringDriverdriverId : Stringname : Stringlocation : Locationavailable : booleanisAvailable() : booleansetAvailable(available : boolean) : voidgetLocation() : LocationLocationx : doubley : doubledistanceFrom(other : Location) : doubleRiderideId : Stringuser : Userdriver : Driversource : Locationdestination : Locationstatus : RideStatusassignDriver(driver : Driver) : voidcompleteRide() : voidgetStatus() : RideStatusRideServicedrivers : List<Driver> source : Location,requestRide(user : User,destination : Location) : Ride completeRide(ride : Ride) : voidDriverMatchingStrategyfindDriver(drivers : List<Driver>, source : Location) : DriverNearestDriverMatchingStrategyfindDriver(drivers : List<Driver>, source : Location) : DriverRideStatusREQUESTEDACCEPTEDCOMPLETEDCANCELLED

Also See

Comments

Popular posts from this blog

Most Frequently Asked Low Level Design(LLD) Interview Questions

Below are the curated list of most commonly asked Low Level Design (LLD) interview problems. Each problem includes a short description and a link to the complete solution with code and class diagrams. Design Parking Lot System The system should handle parking for different vehicle types such as bikes, cars, and trucks. It should manage slot allocation, availability tracking, and entry/exit flow. The design also ensures efficient usage of parking space under varying load conditions. View Solution Design Elevator / Lift System The system should support multiple elevators operating across floors with request handling logic. It focuses on scheduling algorithms to minimize wait time and optimize movement. It also manages direction control and concurrent floor requests. View Solution Design Movie Ticket Booking System The system should allow users to browse movies, select shows, and book seats. It handles seat ...

Designing a Parking Lot - Low Level Design

Problem Statement Design a parking lot that can handle vehicles entering and leaving while managing parking across multiple floors. Each vehicle should be assigned a suitable parking spot based on its type, and the spot should be freed once the vehicle exits. The design should also support generating a ticket at entry and optionally calculating the parking fee based on the duration of stay. Asked In Companies Amazon Google Microsoft Uber Walmart Flipkart Meta PayPal Oracle Salesforce Adobe Apple Intuit LinkedIn Atlassian Functional Requirements The design should support multiple vehicle types such as bikes, cars, and trucks A vehicle must be assigned a parking spot compatible with its type A parking spot cannot be assigned to more than one vehicle at a time The parking lot should support multiple levels (floors) The design should search and allocate an availa...

Design an AI Agent Tool Execution Coordinator

Problem Statement Design an autonomous AI Agent Tool Execution Coordinator (similar to the orchestration cores of LangChain, Semantic Kernel, or Claude Desktop agent runtimes). The engine must coordinate agents by parsing structured tool call requests emitted by a Language Model (LLM), invoking corresponding local Java code methods dynamically by name using reflection, appending execution outputs back to the context history, and driving the agentic loop iteratively until a final synthesized text answer is generated. Additionally, to prevent token limit errors in long-running sessions, the engine must perform proactive context-window pruning, dropping the oldest historical messages while strictly preserving the system instructions template at the head of the context. Asked In Companies OpenAI Anthropic Google Microsoft Design Decisions & Patterns Used Modern agentic AI systems use dynamic tool execution to interact with external databas...