When I started practicing Low-Level Design, I realized that jumping directly into drawing UML diagrams often leads to messy, confusing designs.
So instead, I followed a step-by-step approachβbreaking the system into components, understanding flow, and then translating that into a clean UML.
Hereβs how I approached designing a simplified Uber/Ola system.
π― Problem Statement
Design a ride-booking system that can:
Create a trip (source β destination)
Match a driver
Calculate price
Support extensibility (new pricing logic, ride types, etc.)
Step 1: Identify Core Components & Actors
Start simple. Who are the main actors?
Rider β requests a trip
Driver β fulfills the trip
These are the foundation of the system. Everything else builds around them.
Step 2: Define Features & Assumptions
To avoid overcomplicating early design, I made a few assumptions:
Each driver has one vehicle
One rider books one trip at a time
We focus only on trip creation flow
π This helps narrow scope and focus on correctness over completeness.
Step 3: Outline the Logical Flow (Client Perspective)
Instead of jumping into classes, I first defined the end-to-end flow from client side:
Rider enters source, destination, rideType
β
RideBookingSystem receives request
β
RideService orchestrates flow
β
RideFactory β creates Bike / Auto / Cab
β
StrategyMgr β selects pricing & matching strategy
β
Driver is matched
β
Price is calculated
β
Trip is created
π This step is crucialβit directly drives your class design.
Step 4: Identify Classes & Responsibilities
Core Entities
Trip β represents a ride
Rider / Driver β system users
TripMetaData β context (ratings, locations)
Manager Classes (to organize logic)
TripMgr β manages trip lifecycle
RiderMgr / DriverMgr β manage users
StrategyMgr β selects strategies
π These act as centralized handlers for data and operations.
Step 5: Define Relationships
Understanding relationships is key to a good UML.
Aggregation (loosely coupled)
RiderMgr β Rider
DriverMgr β Driver
π These objects can exist independently.
Composition (tightly coupled)
TripMgr β Trip
π Trip lifecycle is controlled by TripMgr.
Step 6: Apply Design Patterns
To make the system extensible:
πΉ Singleton Pattern
Used for:
- TripMgr
- RiderMgr
- DriverMgr
π Ensures a single source of truth as these classes will be interacting with database.
πΉ Strategy Pattern
Used for:
- Pricing Strategy
- Driver Matching Strategy
PricingStrategy β Default / RatingBased
DriverMatchingStrategy β LeastTimeBased
π This avoids hardcoding logic and allows easy extension.
πΉ Factory Pattern (Ride Selection)
To support multiple ride types:
RideFactory β BikeRide / AutoRide / CabRide
Flow:
- User selects ride type
- RideService delegates creation to RideFactory
- Factory returns appropriate ride type object

π This centralizes ride creation and avoids scattered conditional logic.
Have attached a rough UML diagram for reference
This exercise helped me:
Think in terms of flow before code
Understand where Factory vs Strategy fits
Appreciate the importance of clear system boundaries
Still learningβwould love to hear how others approach this problem π
Top comments (0)