System Design - Reservation System

Subscribe Send me a message home page tags


In this post, we try to present a general template for reservation system design. Reservation system is a general concept and it has many flavors. Here is a list of common examples:

As always, a system is defined by its components and the associated behavior. We will start with defining components used in such system and present general data models. Then we will briefly talk about some APIs commonly used in reservation system. We will finish this post by discussing a little bit the architecture design details.

Components

There are three main categories in a reservation system

User

This is quite obvious. We need user to interact with the system. There are two types of users in a reservation system:

For example, in a restaurant reservation system, customers who book tables are the resource consumers and restaurant owners are the resource providers. Resource providers are relatively "static" compared to resource consumers; they interact less with the reservation system in most cases.

The data for users is straightforward:

data-model-user.png

Group and Resource

Resource is an entity that can provide a service to customer. For example, a table in a restaurant is a resource; a seat in an airplane is also a resource.

Most of the time, resources are grouped together. For example, a restaurant has many tables and similarly an airplane has many seats. In these cases, the restaurant and the airplane are groups (of resources).

The data model is given as follows:

data-model-group-and-resource.png

Group and resource are provided by resource providers and only resource providers can modify them.

Reservation and Reserved Resource

In a reservation record, we should have the information of the user who makes the reservation and the reserved resource. It should also have a time component, which is period during which the resource is reserved. It can happen that a reservation uses multiple resources so we will have a separate table for reserved resource. This provides additional flexibility; on the other hand, when we query the resource availability we need to merge the Reservation and ReservedResource tables together.

data-model-reservation.png

Action

Find Available Resource

The inputs in the query are the time range and some conditions, such as location, type of resource, etc. We will first query the Group table because it contains more meta data of resources. The outcome of this step is a list of groups that meet the requirements.

Then we can collect the following information

And the difference is the available resources. The API is given as follows:

1
List<Resource> findAvailableResource(DateTime startTime, DateTime endTime, Condition conditions);

Make Reservation

Making reservation involves updating the Reservation table and the ReservedResource table. Due to the asynchronous nature of the system, before we reserve a resource we need to verify again the resource in question is still available.

Here is the API:

1
Reservation reserveResource(Resource resource);

Cancel Reservation

Users can cancel reservation if they no longer want to use the resource. To cancel a reservation, we just need to update the Reservation table.

There are different ways to represent a cancelled reservation. We could delete the record from the table or add more fields to the data model. For example, we could add a state field to Reservation record. When a new reservation is created, the state field is set to effective; when a reservation is cancelled, the state is set to cancelled.

Here is the API:

1
boolean cancelReservation(Reservation reservation);

Release Resource

Releasing resource is similar to canceling reservation. The difference is rather semantic. One use case of releaseResource API is when a car leaves a parking lot, the resource is released and the spot becomes available again.

1
boolean releaseResource(Reservation reservation);

Architecture and Other Details

Separate Responsibility

As mentioned earlier, there are two types of users:

There are also two main types of activities:

We could have two separate services to handle these two tasks because we want to follow the divide-and-conquer paradigm and limit the blast radius. For example, we don't want to be in a position where a bug in resource management code has impact on reservation activities.

Caches and Data Partition

At some point in the process, we need to manipulate the database and the data on the disk, which is a very slow operation. Following standard optimization approach, we need to have caches and we need to partition the data.

Consistency Requirement

In general, a reservation system needs to support concurrent requests, i.e. different users can make reservation at the same time. For they may reserve the same resource, most of reservation systems requires strong consistency model. Eventual consistency is not appropriate because it means we may have two customers reserving the same resource for the same period. If we use lock mechanism to achieve stronger consistency, we need to be careful about how we lock a table. For example, for a restaurant booking system, we may have many restaurants stored in the same table and we should avoid locking the entire table, which impacts many restaurants at the same time, only to serve one single user who is making a reservation to a particular restaurant. More specifically, row-low is much preferred.

Data Retention Policy

Data storage is very cheap nowadays so it's not a problem anymore. However, we cannot keep all the historical data otherwise as database tables grow larger, it will take longer to execute the query and we will see a gradual degradation of runtime performance.

We should just keep the needed data in our database and move historical data to some other data archive system such as S3 Glacier.

----- END -----

Welcome to join reddit self-learning community.
Send me a message Subscribe to blog updates

Want some fun stuff?

/static/shopping_demo.png