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:
- Hotel booking system
- Flight booking system
- Restaurant reservation system
- Parking lot
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
- resource
- reservation
User
This is quite obvious. We need user to interact with the system. There are two types of users in a reservation system:
- resource consumer
- resource provider
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:

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:

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.

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
- The resources that the groups have.
- The reserved resource in these groups.
And the difference is the available resources. The API is given as follows:
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:
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:
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.
boolean releaseResource(Reservation reservation);
Architecture and Other Details
Separate Responsibility
As mentioned earlier, there are two types of users:
- Resource consumer
- Resource provider
There are also two main types of activities:
- Resource management
- Reservation management
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 -----
©2019 - 2022 all rights reserved