Microservices by Example: Airbnb

Microservices by Example: Airbnb
Photo by Stephen Wheeler / Unsplash

Welcome to the series Microservices by Example, where we will use real world use cases as example to break and talk about microservice architecture. In today's post we will look at Airbnb and other hotel or property reservation systems.

Disclaimer: Neither I nor this blog post is in any way related to Airbnb, any examples are purely fictional and do not resemble the application design of Airbnb.

High Level Architecture

First off, let's look at the high level architecture of how microservices can be used in an Airbnb-like application:

Example of how Airbnb's cloud architecture could look like

In grey we see our own deployed cloud environment. On the left we the users interacting with the system and on the bottom we identify 2 third party services providing us with authentication (e.g. Okta) and payments (e.g. Stripe or Mollie).

While we could have also build both solutions in our own environment, our fictional CTO has decided that both these domains already have great as-a-Service solutions available and they are worth the cost.

Finally, these services are still high over and can be broken down into smaller subservices to handle different scaling or communication requirements.

Let's break down our Cloud Environment:

CDN

We provide a CDN to make sure we are able to serve larger files closer to our users. For example property images or larger documents are hosted on the CDN and mirrored to multiple regions. This allows our users to have the best experience possible when viewing the app with minimal download times when browsing property photo's.

API Gateway

We have a single point of entry to our API's. It is not possible to interact with any services externally without it passing through the API Gateway. This service allows us to offload and do some pre-checks on the traffic.

In our example, the API Gateway handles the authentication and authorization of API calls to the services by validating any Authorization headers against the JSON Web Key Sets provided by the External IDaaS provider.

This also means that our cloud infrastructure does not deal with any authentication management API endpoints. The app will directly handle authentication with the IDaaS provider without interacting with our services. Once the user is logged in against the IDaaS provider and has an authentication token, it is able to access resources behind the API Gateway.

The API Gateway can validate the scopes in the JWT authentication token against required scopes for each individual service and can forward the request to the microservice.

Microservices

The microservices contain the actual business logic for their domain. Some microservices will need to chat with another service to handle certain business logic, but we should try to keep this to a minimum to prevent "Chatty" services that are no longer loosely coupled.

💡
Microservices can also implement a more Event Driven approach, where instead of directly chatting with another service, they publish a message onto the queue for other services to consume.

Communication

In our microservice architecture, we have decided to communicate using two standards. We decide on a popular and widely used method to communicate with clients: GraphQL; and chose a method that is less suited and common for clients, but easier to use for service-to-service communication: Protobuffers.

GraphQL

GraphQL is used to communicate with the outside world. Any communication going through the API Gateway is using GraphQL as it's language. We want to implement a single standard for external communication to make it easy for our clients to implement and not have to deal with multiple languages or structures. The clients should not need to be bothered with our microservice design choice.

Protobuffers

Protobuffers are used in conjunction with gRPC (Google Remote Procedure Calls), which us to define interfaces in .proto files and then generate both our client and server code and keep these in sync between microservices. Because it communicates directly as binary data, it does not have the overhead of JSON (un)marshalling and also allows streaming data, its performance is much better then GraphQL or JSON. Because we do not have clients involved who need to understand or integrate these APIs, we roll Protobuffers here.

Let's zoom in on the reservation and review service to take a better look into their subservices:

Services using multiple methods of communication

Payment Service

I would also like to specifically zoom in into the Payment Service, because we are also implementing a 3rd party SaaS to handle our payments. However, we would still like to handle our business logic related to payments in this service.

What if our team decides to allow AirBNB subscriptions to get access to exclusive properties or discounts for example. We would also need to integrate payments in a separate services that handles logic related to the subscriptions.

Instead of needing to directly integrate with the 3rd party SaaS for a second time, we would be integrating with our Payment Service, which handles all the business logic.

Now our C-level manager decides they no longer wish to work with a certain payment provider and migrate our new payments unto a different service. We would just need to update our Payment Service to change the implementation with the actual 3rd party service and leave our Protobuf interface as-is. No need to change any services actual needing payments, because we abstracted payments into our own Payment Service.

This allows you to very easily also test blue-green or canary deployments of introducing the new payment gateway, as both services will automatically load balance to the different deployments of the payment service.

Databases

To fully utilize the power of microservices, we have also separated our databases for each service into different clusters. This allows us to choose the correct technology (MySQL/PostgreSQL/NoSQL) for each service, but also allow individual scaling on databases. We do not want one of our microservices to break an entire database cluster causing outage on other microservices ;-).

Kafka and ElasticSearch

Finally, we would like to have a very fast and flexible search service for our clients available so that our platform stays their #1 platform of choice for booking properties. We do not wish to search our database for many statements and also be able to find more and less relevant results. We want to utilize ElasticSearch for this.

ElasticSearch does need to be fed with data and should not be directly connected to our database clusters. Instead, we use Kafka as an event bus to publish any new messages from changes in the database into ElasticSearch.

This allows our search to stay up to date with streaming data from our database. You can read more about how to achieve this here.

Final words

I hope this article helped you get a better grasp at microservice architecture by using Airbnb structure as an example. If you have any interest in a blog series to create an microservice application based on this example, make sure to let me know as I'm contemplating on writing and creating an example app in Golang on GitHub, which can be deployed directly onto Kubernetes.

If you have any questions, feel free to comment or reach out directly on this blog.