Understand the Requirement
In this blog, I have explained how do we design a hotel booking system something very similar to booking.com, MakeMyTrip, or goibigo, but just one thing to call out, we will be looking at a very high-level architecture of the whole system and also at a lower level class diagram and all of that in this blog.
So, before we jump into the problem, let's first look at the functional requirements, and then I will be mentioning also the non-functional requirements of what we want to achieve and then finally I have mentioned the system designing part.
Types of Requirements to be implemented
Here, we have two major consumers of this application. One is the hotel side of users and then there are the consumers who want to book the hotel. It is B2C communication.
- Hotel Users(Business)
- Customer(Consumer)
Functional Requirement
So, from the business side (Hotel side) we'll have these three major functionalities:
1) Hotel Managers should be able to onboard onto our platform.
2) They should be able to update their property for example, they might want to add a new room, or they might want to change the pricing, or they might want to add new images and stuff like that.
3) Then they should be able to see what all bookings are there and along with that, also they want to get some insight into the revenue numbers, etc.
From Consumer Side:
1) From a user standpoint, they should be able to search for a hotel in a particular location with a couple of search criteria. For example, they might want to filter within a price range or some aspects of the hotel like a five-star or a beach view or pool view, etc.
2) Customer should be able to book that hotel.
3) Once customers have booked that hotel, they should be able to view their booking on the website.
4) The customer should get a conformation/notification message on his registered Mobile number /WhatsApp/ Gmail that the booking is confirmed.
These are the main requirements. We should also design it in such a way that we leave room for certain types of analyzes.
Non-Functional Requirement:
1) Now, from a non-functional side of things, we need this platform to run at very low latency.
2) The booking application should be very consistent with high availability and high consistency. I mean if a user is booking a hotel he can book the hotel immediately, and he should get the booking confirmation immediately after booking.
3) The application should be highly scalable. From a scale standpoint let’s assume there are roughly 50000 hotels in the whole of India at this point, and there are roughly 10-12 lakh rooms in all the hotels across the country. At this point, you can assume that there are a hundred rooms in a particular hotel in general.
4) These are some edge cases we should be able to handle that. The reason I am talking about this is supposed to be, that the hotel has a hundred rooms and now this room will be booked for several days. So, there will be a use case where multiple users are planning to book for a specific room. We have to handle these use cases as well.
High Level System Design
Detail Explanation
Now let me explain the overall design of the whole system and how the data flows within each component, then we look individually into some of the components.
So the whole request flow starts at the point, which is a UI that we give out to the hotel managers through a website or a mobile app, they would come on onboard onto our platform and list a property chain located in multiple location and to be listed on multiple platforms like airbnb management london, and the same UI would be used by them to modify the hotel rooms. So, let's say they want to add a new image, or they want to add a new room or if they want to make any modifications then they have to do it from the UI.
Now from the UI, request comes to a Load Balancer through which request comes to a hotel micro service. This is basically a micro service which manages the hotel part which is basically the onboarding and the management.
Let's just say there's an increase in traffic so, there could be multiple nodes of this hotel services that could be added here and so it can become a horizontally scalable component. Now hotel data itself is very much relational data. Also, I mentioned the number of images, and it doesn't even have a scale problem. So, we'll be using a clustered MySQL with one master and multiple slaves.
Slaves can be added as and when required. If at any moment there is a huge spike in Reading traffic, we can add more slaves. But the hotel data resides within the MySQL database. Suppose any image is added, so hotels can add images about the rooms, about their gym, swimming pool and all of that all those images would be stored into a CDN(Content Delivery Network server) and the reference to the CDN, which is basically a URL of the image would be stored in the database, and that URL would be sent out to customers and whenever they want to render an image that, would be looked up directly from the CDN.
CDN is a geographically distributed data store, which we will be using for sending out images/docs throughout the whole world. Suppose I'm using the application from India and somebody's connecting from Australia, and if they want to look up for an image of a particular hotel. Then my request will look upon the CDN server which is in India the other person will look up the CDN server, which is in Australia.
The next thing is basically in a specific scenario where each time a modification is happening to hotel properties If a new hotel gets onboarded to our application, for example, then we want to bubble up this hotel to the users who are going to search for this. There are multiple ways in which we can send out this information. We'll be using a Kafka here, so each modification that is happening within the hotel service will pass through a Kafka cluster, and there'll be multiple consumers that will be sitting on top of this cluster, which will populate their data store for serving the search request.
So, one of the Kafka consumers will be the SearchHotel consumer. Suppose a hotel gets a new room, so in this scenario, there will be a payload that is given to Kafka, which has all the information about the hotel details that is required. The SearchHotel consumer pulls up the payload from Kafka, and it stores it into its database, and this database would be used to provide the search Hotel data on the website.
Now for the search capabilities, I am here using an Elastic search. Elasticsearch is a database that is built on the Lucene platform. Similarly, instead of elastic search, you can also use a Solr from apache here. Both are kind of similar components ideally it would depend on what infrastructure is being used in your company you could use that.
But the idea of using elastic search is that I want our search to be supporting fuzzy search means suppose a user is searching for a hotel in Singapore, the user might not know the correct spelling. If they type in the wrong word, then our application should not give them any search results. I would want our application to be able to support a fuzzy search.
So, all the data of each hotel flows through Kafka via the searchHotel consumer into the elastic search cluster. On top of this elastic search sits the Search Service. If there's a spike in traffic, we can increase the number of nodes in the Kafka cluster, and also, we can increase the number of search hotel consumers.
Our Search Service is the service that powers the search on the website. The user, when he/she tries to book a specific hotel, then ideally request comes to the backend through again a load balancer to the search Hotel service whenever they want to search for a particular hotel they will have to give a date range and a location. As a search criterion and along with that, they could also provide some tags and these tags would be the properties of the hotels. A five-star category is a tag. A sea-view category is a tag.
Now the search on elastic search would be happening on either of these tags and the date ranges that are provided. So, this eventually takes care of the search flow. Now once the user has seen some of the hotel search results on the website, they would want to book a hotel. The booking again happens through the UI. Now a hotel booking request again flows to this load balancer and talks to BookHotel Service. BookHotel Service essentially sits on top of a MySQL database.
Whenever a booking happens, that booking gets stored into this MySQL DB. Then request comes to a Payment Service. Normally when a booking request comes, it stores something, it will send the request for payment, once there's a success, it will mark the booking confirmation. Now again, whenever a booking is happening, the data is flowing into the same Kafka.
Suppose there is just one room available in a hotel and assuming that room is already booked. We want to make sure that this hotel should not be available during a search in that same date range because it's not available. So, all of that information is again sent to the same Kafka, which is read by Search Consumer, and then it takes care of even removing the hotels which are now completely booked.
There is an Archival Service here in our system. Let's say booking is canceled or booking is completed, the request will move through the archival service to a Cassandra cluster. The reason we should use a Cassandra here is that it is a very good database that can handle a huge amount of reads and writes. It has a constraint that it needs a partition key on which all the queries should happen.
Rest API Designing
Now, java spring boot developers have mentioned below the rest APIs we need for our backend services.
From Hotel Side
1. There'll be a POST API: POST /aegis/register/hotels to create/onboard a hotel which will be part of their onboarding process.
2. There will be a GET API with an id GET /aegis/register/hotels/{hotel_id} which will give back the information of the hotel details, which can be rendered on the screen, and the hotel manager can see it.
3. There will be a PUT API: PUT /aegis/updateHotel/hotels/id which will be used to update any information or image of a hotel.
4. Similarly, there will be a PUT API: PUT /aegis/updateHotel/hotels /{hotel_id}/room/{room_id} which would be used to update the room information or create new rooms. There can be a lot more APIs that you can add when you know there's a requirement to add.
DB Schema
Now let's look at how the DB schema might look like how I have mentioned here.
There will be a Hotel schema and another one is Room schema. Below is the details of the tables for User and Hotel.
Schema Name | Table Name | Column Details |
---|---|---|
AEGIS_HOTEL | hotel | Id, name, pin, image_hotel, description |
hotel-service | Id, hotel_id, amenities_id, description | |
room-info | Id, hotel_id, room_facility_id, room_info, current_price, discount_percentage | |
room-facility | Id, room_id, amenities_id | |
amenities | Id, info, display_Image | |
locality | Id, country_code, Pin, state_code | |
AEGIS_ROOMS | booking-info | Id, room_id, user_id, from_date. To_date, booking_status |
rooms-availability | room_id, from_date, to_date, number_of_rooms | |
booking-status | booked, pending, cancelled, reserved |
Internal Functions of Booking a Hotel
Here, what Redis does is, it has something called callbacks so, one of the later versions of Redis has introduced this concept called callbacks so, whenever a key is getting expired, you'll get a notification, okay. And you can do whatever you need to do then, right. So, if you get a Success notification from payment, well and good. Success notification means the payment has gone through then you will mark the booking as BOOKED.
But before that, if you get a call back from Redis saying that the key has expired, and you've not got the success from the payment you will say that the booking is CANCELLED. Alternatively, you could also get a failure from payment saying for whatever reason, the payment didn't go through and you got a failure response from the Payment Service, in that again you can say CANCELLED.
From USER Side API calls
1)There'll be a POST API: POST /aegis/book/hotels to book a room in the specific hotel where on the request body it takes hotel_id, room_id, number_of_rooms, start_date, end_date.
2)There will be a GET API with an id GET /aegis/search/hotels/{hotel_id} which will give back the information of the hotel details that the user wants to book.
3)There will be a PUT API: PUT /aegis/feedback/hotels/id which, will be used to update any comments or feedback that the user wants to write about this hotel.
These all APIs from the user side take care of checking the available rooms in a hotel, book the hotel and allow the user to give ratings or feedback comments about the hotel service.
Service Methods
- guestAccoutRegistration(access_Key, username, pass, address);
- hotelAccountRegistartion(access_Key, username, pass, address);
- For hotel managers, we have to create Method
- registerHotel(access_Key, location, from_Date, to_Date, room_details, description);
- searchHotelService(access_Key, location, checkin_Date, check_out_Date, guestAccount_id);
- viewRoomFacilities(access_Key, location, checkin_Date, check_out_Date, guestAccount_id);
- bookHotelService(access_key, payment_info, room_id, checkin_Date, check_out_Date, guestAccount_id);
Steps:
- Check for the availability of Rooms in a hotel
- Allow for booking a room if it is available
- When book hotel API is called then the service inserts in the booking table and reduce the available room count
- Then this information is put on the Redis cache and also into Kafka
- Then payment service is called which takes care of booking completion process once payment is successfully done
Here, we are using Solr as a search server where we can have the document-based data, and we can index that, information and can fetch the data directly from Solr. Here the performance is going to be better.
Here the bookHotel Service does the transaction management. The guest has to figure out which room he has to book. He will go through the description from UI to understand them, what are all amenities the Hotel is providing, etc., and after he decides to book a specific room. As soon as he clicks the book button, then he will be displayed the payment screen, where he/she has to fill in all the card details for booking.
Once he clicks on confirm booking, the bookHotel service is called then, the request will go to the Hotel DB, and in that, it will change the status to pending, and after that, the bookHotel service will invoke the Payment service. The Payment service will internally call the Payment gateway, where it validates the user input of credit card or debit card info. And after payment is a success, then again from Payment service request goes back to bookHotel Service to update the status to “BOOKED” in the Hotel DB. It is how the hotel booking application backend works.
Here, I am using Log parser because it does mainly 3 different operations. It detects the changes insert, update or delete from the logs, and it will convert it to different objects, and then it broadcast the data or information to different locations like Kafka, etc. based on our needs.
So, from the log parser, information is sent to Kafka in JSON format. Once it comes to Kafka, then from here, it goes to the Persistent service. This service passes the information to the Search broker like Solr or Elastic search. Also, Kafka passes the information to the Redis cache.
I am using Redis here because in Redis, we will be storing all the information like insert update or delete, and we can use this information to do Full Indexing based on the needs. It is kind of a fault-tolerant mechanism.
Another purpose of using Redis Cache is we can share the data from the Redis cluster to different other systems. As Redis is an in-memory database, so whatever data we will be accessing from Redis is going to be faster as compared to Oracle or MySQL Database.