Design Deliveroo
Published:
Deliveroo System Design
This post provides a detailed system design for Deliveroo, including functional and non-functional requirements, capacity estimations, database schema, API design, microservice architecture, and detailed operation flows.
Functional Requirements
The following core features constitute the minimum viable product (MVP):
- Search & Discovery: Customers can search for restaurants and view menus.
- Ordering: Customers can select items and place orders.
- Order Acceptance: Restaurants receive and accept or reject orders.
- Dispatching: The system assigns a rider to the order automatically or manually.
- Tracking: Customers can track the rider’s location in real-time.
Non-Functional Requirements
Key system qualities include:
- High Availability: Ensure system operability during peak hours (e.g., lunch/dinner).
- Low Latency: Critical for browsing menus and tracking riders.
- Scalability: Handle spikes in traffic, such as Friday night dinner rush.
Consistency Trade-offs (CAP Theorem)
- Strong Consistency: Required for payments and order inventory to prevent double-booking.
- Eventual Consistency: Acceptable for rider location and menu/pricing updates to prioritize availability.
Capacity Estimations
Traffic & Volume Estimates
- Total Users: 100 million
- Daily Active Users (DAU) placing orders: ~1 million (1% of total)
- Total Orders Per Day: 1 million
Throughput (Requests Per Second)
| Metric | Average Load | Peak Load (10x) |
|---|---|---|
| Orders (Writes) | ~12 RPS | ~120 RPS |
| Browsing (Reads) | ~120 RPS | ~1,200 RPS |
| Rider Updates | 200,000 RPS | 200,000 RPS |
Rider updates occur continuously while riders are active and represent a consistently high load.
Architecture Takeaways
- Order Service: Low volume (~120 RPS) but requires strong consistency (ACID transactions).
- Rider Location Service: Extremely high volume (200k RPS), data is ephemeral and supports eventual consistency.
Database Schema
1. User Service
users
- user_id (PK)
- name
- email (Unique)
- phone_number
- password_hash
saved_addresses
- address_id (PK)
- user_id (FK)
- label (e.g., “Home”, “Work”)
- street_address
- city
- zip_code
- latitude / longitude
2. Restaurant Service
restaurants
- restaurant_id (PK)
- name
- phone_contact
- is_active (Boolean)
- cuisine_type
restaurant_hours
- restaurant_id (FK)
- day_of_week (0=Sunday)
- open_time
- close_time
menu_items
- item_id (PK)
- restaurant_id (FK)
- name
- description
- base_price
- is_available (Boolean)
3. Order Service
orders
- order_id (PK)
- user_id (FK)
- restaurant_id (FK)
- rider_id (FK, Nullable)
- delivery_address_id (FK)
- status (PLACED, PREPARING, PICKED_UP, DELIVERED, CANCELLED)
- total_amount
- created_at
order_items
- order_id (FK)
- item_id (FK)
- quantity
- price_at_purchase
4. Rider Service
riders
- rider_id (PK)
- name
- phone_number
- vehicle_type
- current_status (OFFLINE, IDLE, BUSY)
Live GPS coordinates are stored in Redis, not in the database.
API Design
Consumer-Facing APIs
Place Order:
- POST
/v1/orders - Request:
{ "restaurant_id": "r_123", "address_id": "addr_555", "items": [ { "item_id": "i_99", "quantity": 1, "options": ["spicy"] } ] } - Response:
{ "order_id": "o_777", "status": "PLACED" }
Get Order Status: GET /v1/orders/{order_id}
- Response:
{ "order_id": "o_777", "status": "PICKED_UP", "rider_id": "rid_789", "delivery_address_id": "addr_555", "items": [ { "item_id": "i_99", "quantity": 1, "name": "Spicy Chicken Wings", "price": 12.00 } ], "total_amount": 25.00, "created_at": "2026-01-06T12:00:00Z", "rider_location": { "lat": 51.5074, "long": -0.1278, "timestamp": 1678901234 } }
Get Order History: GET /v1/orders?limit=10&offset=0
- Response:
[ { "order_id": "o_776", "status": "DELIVERED", "total_amount": 18.50, "created_at": "2026-01-05T18:30:00Z" }, { "order_id": "o_777", "status": "PICKED_UP", "total_amount": 25.00, "created_at": "2026-01-06T12:00:00Z" } ]
Restaurant Search: GET /restaurants/search
- Response:
[ { "restaurant_id": "r_123", "name": "London Pizza", "cuisine_type": "Italian", "is_active": true } ]
Menu Fetch: GET /restaurants/{id}/menu
- Response:
[ { "item_id": "i_99", "name": "Spicy Chicken Wings", "description": "Hot and crispy wings with special sauce", "base_price": 12.00, "is_available": true }, { "item_id": "i_100", "name": "Margherita Pizza", "description": "Classic cheese and tomato", "base_price": 10.00, "is_available": true } ]
Restaurant APIs
- Poll for new orders: GET
/v1/orders?status=PLACED&restaurant_id={id} - Accept/update order: PATCH
/v1/orders/{order_id}/status{ "status": "ACCEPTED", "prep_time_minutes": 15 } - Menu management: POST
/v1/restaurants/{id}/menu-items(add), PATCH/v1/restaurants/{id}/menu-items/{item_id}(update availability)
Rider APIs
- Location updates (200k RPS): POST
/v1/rider/location{ "lat": 51.507, "long": -0.127, "timestamp": 1678900000 } - Job management:
- POST
/v1/orders/{id}/accept - POST
/v1/orders/{id}/pickup - POST
/v1/orders/{id}/deliver
- POST
Internal Services
- Dispatch Service: POST
/internal/dispatch/match(Input: order details, Output: best rider) - Payment Service: POST
/internal/payments/charge
End-to-End Operation Flows
Customer Flow
- The customer searches for restaurants and browses menus via the Restaurant & Menu service.
GET /restaurants/search?query=pizza&location=London - When placing an order, the Order service validates the request, calculates totals, and creates an order record with status
PLACED.POST /v1/orders - The Payment service processes the customer’s payment securely and updates order payment status.
POST /internal/payments/charge - Upon successful payment, the Dispatch service is triggered to assign the most optimal rider automatically based on proximity, availability, and workload.
POST /internal/dispatch/match - The customer receives real-time push notifications about order acceptance, rider assignment, and delivery status via the Notification service.
- The customer can track the rider’s live location through the Driver Location service, which streams GPS updates in real-time.
GET /v1/orders/o_777/status
Restaurant Flow
- Restaurants poll for new orders from the Order service.
GET /v1/orders?status=PLACED&restaurant_id=r_123 - Upon receiving an order, the restaurant accepts or rejects it by updating order status.
PATCH /v1/orders/o_777/status - Once accepted, the restaurant prepares the order and updates the status to
PREPARING.PATCH /v1/orders/o_777/status - Real-time push notifications notify the restaurant of rider assignment and order pickup.
- The restaurant can manage menu items and availability through the Restaurant & Menu service APIs.
POST /v1/restaurants/r_123/menu-items
Rider Flow
- Riders continuously send location updates to the Driver Location service at high throughput.
POST /v1/rider/location - When a new order is assigned by the Dispatch service, riders receive a notification with order details.
- Riders accept the job, update status to
BUSY, and proceed to pick up the orderPOST /v1/orders/o_777/accept - Upon pickup, the rider updates the order status to
PICKED_UP.POST /v1/orders/o_777/pickup - Riders deliver the order and update the status to
DELIVERED.POST /v1/orders/o_777/deliver - Throughout the delivery, the rider’s live location is streamed for customer tracking.
Payment Flow
- When an order is placed, the Payment service securely charges the customer’s payment method.
POST /internal/payments/charge - Payment status is updated in the Order service to ensure strong consistency.
- The Payment service handles refunds or cancellations if needed.
POST /internal/payments/refund - Payment events trigger notifications to customers and restaurants about payment confirmation or issues.