...

/

System Design: Designing a Meal Delivery Application

System Design: Designing a Meal Delivery Application

An example interview question on designing a meal delivery application.

Question

Design a meal delivery application.

Background

Like most system design questions, this question is very open-ended and can go in many different directions. This question in particular has many different moving parts: multiple client apps (customer, restaurant, or delivery driver), complex business logic (i.e., pricing), and third-party integrations for mapping and payment processing, etc. During the interview, the interviewer may want to dive into one area, so it’s important to establish the discussion in a structured way that allows for this to happen naturally.

Solution approach

We’ll use the following framework to help structure our answer. We’ll also use this for future system design questions in this course. This may not be the best or most comprehensive framework, but it should provide a baseline to start the discussion:

  • Define system requirements:

    Before we begin any design, we’ll spend a few minutes agreeing on the one to two primary use cases we’ll need to cover.

  • System breakdown:

    We’ll then diagram the key individual components of the system needed to address the one to two primary use cases.

  • Dataflow discussion:

    We’ll discuss the necessary data points the system will need to collect and how they flow through the system.

  • Scaling the design:

    We’ll talk about design choices and trade-offs to ensure that the system scales.

  • Capacity modeling:

    We’ll estimate the request volume and data volume that the system will need to handle for a given time period.

Sample answer

Requirements

For this problem, we’ll focus on the following two use cases:

  • Nearby restaurants feed:

    When the user opens the app, the user is able to see a list of nearby restaurants that are available for delivery to order from.

  • Food delivery ordering:

    After a user submits an order, the order is processed and delivered.

We’re not covering a lot of other potential requirements, such as the onboarding of delivery drivers and restaurants and user authentication. This is mainly to keep this exercise scoped, but it may be a good followup to think through how these requirements might be handled.

Given the complexity of each use case, we’ll do a separate system breakdown, dataflow discussion, and scaling the design section for each use case.

System breakdown: Nearby restaurants feed

This is a potential architecture for the first use case. Although, we will re-use some components for the second use case as well.

Dataflow discussion: Nearby restaurants feed

  1. When the user first opens the application, the Client Application will send a request to the server consisting of (user_id, user_location) to request a feed of nearby restaurants on behalf of the user.

  2. This request will be received by a Load Balancer which will do basic request handling (i.e., TLS termination), identify the request type as a “request nearby restaurants” request, and route the request to an available application server running an instance of the Get User Feed Service.

  3. The Get User Feed Service is responsible for returning a list of nearby restaurants and their corresponding menu items. This is ordered using a predetermined ranking algorithm (i.e., user ratings or user preference). It would be very slow and expensive to calculate this feed on every single user request, so the system should pre-generate this feed and store them in an in-memory data store for fast retrieval on every user request. However, the Get User Feed Service will first need to translate the specific user location (i.e., a specific latitude/longitude pair) to a more generic geo_id that represents a wider geolocation delivery area. The reason for this is that it would be infeasible to store the specific latitude/longitude pair of every location that a restaurant could deliver to. Instead, our system will specify broader “geolocation delivery areas” that a restaurant will deliver to (say, between two to five geolocation delivery areas depending on the size of the area). The details of how this works is out of scope for this question, but it’s important to note that this translation needs to happen for the system to scale.

    3a. The Get User Feed Service will send the user_location to a User Location Mapping Service to translate the user_location to a “geolocation” delivery area. It will then return the corresponding geo_id.

    3b. The Get User Feed Service will then do a series of key-value lookups using the user_id it receives from the Client Application and the recently retrieved geo_id to obtain the necessary data to return to the user. It will perform these lookups against these two “warm,” in-memory, and key-value data stores:

    • Restaurants By User + Location:

      Users will mostly order from one to two locations (i.e., their home or work), and they will likely have certain preferences. The goal of this datastore is to store a personalized and ordered list of restaurants by user and location. This data store has (user_id, geo_id) as its key where the user_id corresponds to a user and the geo_id corresponds to a frequent geolocation delivery area that the user orders from. The value per key is a list of restaurants (i.e., a list of restaurant_ids) that deliver to that geolocation that is ordered by a predetermined “per-user” ranking algorithm. This data store may store N rows per user where N means the top N geolocation delivery areas a user orders from. First, the Get User Feed Service will attempt to do a lookup against this store to retrieve a personalized list of restaurants by (user_id, geo_id). However, it is not guaranteed that a (user_id, geo_id) pair exists in this store. For example, the user could be ordering from a new location or the user is new. Therefore, we need an additional data store to handle this ...