> For the complete documentation index, see [llms.txt](https://mayanktyagi3111.gitbook.io/interview-prep/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mayanktyagi3111.gitbook.io/interview-prep/lld-questions/uber-sytem-lld.md).

# Uber Sytem LLD

#### Low-Level Design (LLD) for an Application like **Uber** (Ride-Hailing Service)

In this Low-Level Design (LLD) for a **Ride-Hailing Service** like **Uber**, we will focus on core functionality such as **rider management**, **driver management**, **ride matching**, **ride tracking**, **pricing**, and **notifications**. We'll also employ design patterns to ensure that the system is maintainable and scalable.

We will discuss the following key questions for designing the system:

* **Entities and Relationships**: What are the entities involved in this application? How do they relate to each other?
* **User Management**: How will we manage riders and drivers?
* **Ride Matching**: How will the system match riders with available drivers?
* **Pricing**: How will the fare be calculated based on distance, time, and type of ride?
* **Notification System**: How will the system notify users (riders and drivers) about ride requests, cancellations, or other important events?
* **Ride Tracking**: How will the system track the status and location of ongoing rides?

#### **Key Components**

1. **User Management**: For managing riders and drivers.
2. **Ride Management**: To handle ride requests, ride status, and ride completion.
3. **Ride Matching**: Algorithm to match riders with nearby available drivers.
4. **Pricing Engine**: For calculating the fare for a ride.
5. **Notification System**: To send updates to users about ride status.
6. **Location Tracking**: To track the location of drivers and riders in real-time.

#### **Design Patterns Used**

* **Factory Pattern**: For creating user objects (Riders, Drivers).
* **Strategy Pattern**: For different pricing models.
* **Observer Pattern**: For notifying riders and drivers about ride updates.
* **Command Pattern**: For processing actions such as ride requests, cancellations, and updates.
* **Singleton Pattern**: For managing shared resources like the **RideMatchingEngine** and **NotificationService**.

***

#### **Classes and Data Structures**

**1. User Class (Abstract Class)**

We will create an abstract **User** class, which will be extended by **Rider** and **Driver**.

```java
abstract class User {
    protected String name;
    protected String phoneNumber;
    protected String email;

    public User(String name, String phoneNumber, String email) {
        this.name = name;
        this.phoneNumber = phoneNumber;
        this.email = email;
    }

    public abstract void performAction();
}

class Rider extends User {
    private String location;

    public Rider(String name, String phoneNumber, String email, String location) {
        super(name, phoneNumber, email);
        this.location = location;
    }

    public void performAction() {
        System.out.println(name + " is looking for a ride.");
    }

    public String getLocation() {
        return location;
    }
}

class Driver extends User {
    private String location;
    private boolean isAvailable;

    public Driver(String name, String phoneNumber, String email, String location) {
        super(name, phoneNumber, email);
        this.location = location;
        this.isAvailable = true; // Driver is initially available
    }

    public void performAction() {
        System.out.println(name + " is available to take rides.");
    }

    public String getLocation() {
        return location;
    }

    public boolean isAvailable() {
        return isAvailable;
    }

    public void setAvailability(boolean isAvailable) {
        this.isAvailable = isAvailable;
    }
}
```

**2. UserFactory Class (Factory Pattern)**

The **UserFactory** class implements the **Factory Pattern** to create instances of **Rider** and **Driver**.

```java
class UserFactory {
    public static User createUser(String type, String name, String phoneNumber, String email, String location) {
        switch (type) {
            case "Rider":
                return new Rider(name, phoneNumber, email, location);
            case "Driver":
                return new Driver(name, phoneNumber, email, location);
            default:
                throw new IllegalArgumentException("Invalid user type");
        }
    }
}
```

**3. Ride Class**

The **Ride** class manages the details of each ride, including its status (requested, in-progress, completed, etc.).

```java
class Ride {
    private Rider rider;
    private Driver driver;
    private String startLocation;
    private String endLocation;
    private String status; // "Requested", "In-Progress", "Completed"
    private double fare;

    public Ride(Rider rider, Driver driver, String startLocation, String endLocation) {
        this.rider = rider;
        this.driver = driver;
        this.startLocation = startLocation;
        this.endLocation = endLocation;
        this.status = "Requested";
    }

    public void startRide() {
        this.status = "In-Progress";
        System.out.println("Ride started from " + startLocation + " to " + endLocation);
    }

    public void completeRide(double fare) {
        this.status = "Completed";
        this.fare = fare;
        System.out.println("Ride completed. Fare: " + fare);
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public double getFare() {
        return fare;
    }
}
```

**4. RideMatchingEngine Class (Singleton Pattern)**

The **RideMatchingEngine** is responsible for matching riders with available drivers.

```java
class RideMatchingEngine {
    private static RideMatchingEngine instance;

    private RideMatchingEngine() {}

    public static RideMatchingEngine getInstance() {
        if (instance == null) {
            instance = new RideMatchingEngine();
        }
        return instance;
    }

    public Driver matchDriver(Rider rider, List<Driver> drivers) {
        for (Driver driver : drivers) {
            if (driver.isAvailable() && !driver.getLocation().equals(rider.getLocation())) {
                return driver;
            }
        }
        return null; // No driver found
    }
}
```

**5. PricingEngine Class (Strategy Pattern)**

The **PricingEngine** uses the **Strategy Pattern** to allow different pricing strategies based on distance, time, and surge pricing.

```java
interface PricingStrategy {
    double calculateFare(double distance, double time);
}

class StandardPricingStrategy implements PricingStrategy {
    @Override
    public double calculateFare(double distance, double time) {
        return (distance * 1.5) + (time * 0.5);
    }
}

class SurgePricingStrategy implements PricingStrategy {
    @Override
    public double calculateFare(double distance, double time) {
        return (distance * 2.0) + (time * 0.75);
    }
}

class PricingEngine {
    private PricingStrategy pricingStrategy;

    public PricingEngine(PricingStrategy pricingStrategy) {
        this.pricingStrategy = pricingStrategy;
    }

    public double calculateFare(double distance, double time) {
        return pricingStrategy.calculateFare(distance, time);
    }
}
```

**6. Notification System (Observer Pattern)**

The **NotificationSystem** is responsible for notifying riders and drivers about ride updates (e.g., matching found, ride started, ride completed).

```java
interface Observer {
    void update(String message);
}

class RiderObserver implements Observer {
    private Rider rider;

    public RiderObserver(Rider rider) {
        this.rider = rider;
    }

    @Override
    public void update(String message) {
        System.out.println("Notification to Rider: " + message);
    }
}

class DriverObserver implements Observer {
    private Driver driver;

    public DriverObserver(Driver driver) {
        this.driver = driver;
    }

    @Override
    public void update(String message) {
        System.out.println("Notification to Driver: " + message);
    }
}

class NotificationSystem {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}
```

**7. RideRequestCommand (Command Pattern)**

The **Command Pattern** is used to process the actions of requesting, starting, and completing a ride.

```java
interface Command {
    void execute();
}

class RideRequestCommand implements Command {
    private RideMatchingEngine rideMatchingEngine;
    private Rider rider;
    private List<Driver> drivers;
    private NotificationSystem notificationSystem;

    public RideRequestCommand(RideMatchingEngine rideMatchingEngine, Rider rider, List<Driver> drivers, NotificationSystem notificationSystem) {
        this.rideMatchingEngine = rideMatchingEngine;
        this.rider = rider;
        this.drivers = drivers;
        this.notificationSystem = notificationSystem;
    }

    @Override
    public void execute() {
        Driver driver = rideMatchingEngine.matchDriver(rider, drivers);
        if (driver != null) {
            notificationSystem.notifyObservers("Driver matched for rider " + rider.getLocation());
        } else {
            notificationSystem.notifyObservers("No drivers available for rider " + rider.getLocation());
        }
    }
}
```

#### **Main Class to Demonstrate the System**

```java
public class UberApplication {
    public static void main(String[] args) {
        // Create users using the factory
        Rider rider = (Rider) UserFactory.createUser("Rider", "John", "1234567890", "john@example.com", "Location A");
        Driver driver = (Driver) UserFactory.createUser("Driver", "Alex", "0987654321", "alex@example.com", "Location B");

        // Create a notification system
        NotificationSystem notificationSystem = new NotificationSystem();
        notificationSystem.addObserver(new RiderObserver(rider));
        notificationSystem.addObserver(new DriverObserver(driver));

        // Create ride matching engine
        RideMatchingEngine rideMatchingEngine = RideMatchingEngine.getInstance();

        // Create and execute ride request command
        Command rideRequestCommand = new RideRequestCommand(rideMatchingEngine, rider, List.of(driver), notificationSystem);
        rideRequestCommand.execute();
    }
}
```

***

#### **Explanation of Design Patterns Used**

1. **Factory Pattern**:
   * The **UserFactory** class dynamically creates **Rider** and **Driver** objects based on input.
2. **Strategy Pattern**:
   * The **PricingStrategy** interface and its implementations (**StandardPricingStrategy**, **SurgePricingStrategy**) allow different pricing strategies to be applied.
3. **Observer Pattern**:
   * The **NotificationSystem** uses the Observer pattern to notify **Rider** and **Driver** observers about ride updates.
4. **Command Pattern**:
   * The **Command** interface and **RideRequestCommand** class encapsulate the logic for requesting rides, improving modularity and flexibility.
5. **Singleton Pattern**:
   * The **RideMatchingEngine** is implemented as a Singleton to ensure that there is only one instance managing ride matching.

***

#### **Conclusion**

This **Low-Level Design** of the **Uber-like Application** handles core functionality such as user management, ride matching, pricing, notifications, and ride tracking efficiently using design patterns like **Factory**, **Strategy**, **Observer**, **Command**, and **Singleton**. This design ensures scalability, maintainability, and flexibility, allowing future features such as dynamic pricing or multi-ride requests to be easily integrated.
