How we built the "Favourites" feature on the Airasia superapp.
Published: 2023-02-12 05:20:36

###Introduction
Since the internet's inception, application have always provided users with the ability to express emotional proximity or affection to items and content, via liking the content, making it a favourite or outrightly saving it. Many web and mobile applications mimic this behavior by usually allowing users to click on a heart icon or a thumbs up icon. The early years of social media popularized this interaction due to the fact that it was one of the only ways to show appreciation to content produced by a family member, close friend or a stranger. This article intends to describe our implementation of the like/favourite feature by presenting the problem, outlining the methodology, showcasing the solution and touching on some challenges. The implementation will mostly be done from a backend engineering perspective.

###Problem
The absence of such an integral feature impedes a user's experience of expressing emotional contentment to content on a web application. Preventing a user from emotionally connecting with your application in such a manner reduces the probability of purchase. Emotions are a core factor that drives purchase.
On the Airasia superapp, users are allowed to browse through a multitude of goods and services such as flights, food, rides and groceries. A user will have to manually keep track of items they like as opposed to liking it and viewing their list of favourites when they so please.

The inability to access the most prefered items lengthens the track to purchase by increasing search time while sometimes inciting doubt.

The absence of a favourite section means there is little to no information on the items within the user's budget, the user's favourite stores or locations in which the user may like to shop.

###Objectives
The main objective for building a service like this is to ultimately enhance the user experience of the application.
UX enhancements mean a possible increase in user retention and frequency.

This service intends to aid the user to better track goods or services while browsing or for later use. This helps a user compare products, share tracked products with family and friends, stall purchase to check potential reviews or use the product as a reference in any way shape or form.
This service also helps in the curation of data for a recommendation system. The collection of a user's favourites is a direct route to knowing what the user likes which means being able to recommend similar products based on location, availability or in some cases alerting the user to promotions that includes a product they like.

Secondary objectives for this service include enhanced data review in the context of user-product interaction before purchase, research to eventually increase revenue and transactions, and investigation should the need be.

###Methodology
Based on the outlined problem, design and product teams had continued discussions on how to present such a feature to the user. This includes feasibility analysis, user story refinement, mockups across different stages of the user story and high level diagrams.

![Mockups](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ie6g2m1h66fk1sr2ekim.png)


The objective with respect to design is to give the user a clear and open interaction route through minimalism without shrouding the view with text. The heart icon is placed on the image view to allow a perceived direct interaction with the product.

A high level interaction diagram was then drawn to show the relationship between various clients and the favourite service. This proposed diagram is a soft boundary which serves as a guide through which all granular services and components may interact.


![high level interaction diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8xeer7mg4gbsycwbsmww.png)


###Design/architecture
In projects like these, it is highly important to keep regular and up-to-date documentation as design and development is an iterative process through which efficiency and effectiveness is perfected based on tradeoffs and drawbacks. Documentation like this helps create a source of truth for all teams involved.
Tools
The tools involved in this project include

- Google cloud run
- Google pubsub
- Nodejs (Koa)
- Redis cache
- Google Firestore

![architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pbnlwujpuq5413aeoqqn.png)



The favourite service at its core is built based on a data synchronization design pattern. Though not extremely time sensitive, the favourite service is built on the premise of maintaining consistency between user actions and their list of favourite. 

Being that GCP is our cloud service provider, the Google cloud run (GCR) is the best choice for the creation of this service. GCR is most suitable for start-&-stop operational tasks that do not need constant up time. The provisions of scaling and a pay-as-you-go paradigm on GCR maximises the use of functionality while saving cost during low interaction or usage. GCR also provides the option of setting a minimum instance that allows for a quick response upon the first request. All of these make for a very reactive and agile system which improves user satisfaction.

The favourite service is one that provides information that is critical to driving up sales, therefore such information should be readily available to be tapped into by numerous sources. Using Google's Cloud PubSub handles this requirement by encouraging the publishing of important events to respective topics which can be subscribed to by any party that deems this information important. This helps maintain the single responsibility of the service while decoupling it of interactions with other services.

The favourite service publishes primary and secondary information.
In this context primary information is that which governs the item that is being favorite or unfavorited. 
The primary information has both an ownership component and a core component. The ownership component describes the entity tied to this action such as the user and the line of business connected to the product or service. Whereas the core component describes the favorited item in question and all related information.

Secondary information refers to information that can generally be considered as metadata for the favorited product. Though the secondary information also has a core component, its importance is related to its metadata component. Metadata in this context refers to the action (favorited, unfavorited, opened,shared or bought) performed on the item as well as the time of the action.

Caching is very integral to the experience of an application, which is why the favorite service uses redis to implement its caching strategy. The caching strategy used for this project is Cache-Aside also known as lazy loading. This strategy enhances the speed on retrieval based on the ownership component of the data (user's ID). If the requested item already sits in the cache, the roundtrip is cut short by eliminating the trip to the database. However when new data is added for a particular user, the cache is cleared to make room for new queries pertaining to that user.

Google Firestore is the main data hub that stores all information of favorited items and relating actions. The quick lookup speeds of Google Firestore help to adequately retrieve data even with very complex queries.

###Solution
From a backend perspective, the usual CRUD operations endpoints were developed for basic interaction with data through the favorite service. The differentiating factor with these endpoints is the addition of authentication and authorisation baked into the requests and responses through these endpoints. This is to maintain security from a user, admin and client perspective while relieving caller of work to prove their identity.

Specific endpoints were written for the retrieval of secondary information to add activities such as analysis, investigations and data preparations for recommendation as well as predictive systems.

From a frontend perspective, a small component to aid interaction with the product or service is probably the most essential aspect of the project. The main intention was provide a lightweight minimal component that renders swiftly. 

![Like Modal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lno48hu96n6zp19ujg5s.png)


Another important component is one that displays the list of favorited items either from a user or admin perspective. 

![Favourites list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u7xy859ssu16vl18z0c2.png)


The backend service makes the soil fertile for frontend items like admin dashboards and data story previews (graphs and histograms) for the use of data teams.

###Challenges
####Data mutability
One of the main challenges while designing this system was combining both mutable and immutable data into one object. Being that this was a iterative design process, data about the product that could be changed by the other teams was not confirmed initially. 
However throughout the development and testing process, the teams started to differentiate data that was more likely to change (prices and images) from data that was unlikely to frequently change. 

Providing distinction for this is important because immutable data can be used to pre render items for a list of favorited items from the front end. To house mutable components of the data, we used a free form object with little input criteria to allow lines of businesses the freedom of information entry.


![Schema](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfsss24sbmw1vdgk2nt1.png)



####Tool specific challenge
Another challenge specific to this service and google cloud run was that our health endpoint refused to work for some reason. After a few hours of investigation, [the team realized that the word "health" is reserved for direct google endpoints which makes it unusable](https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fstreamlit%2Fstreamlit%2Fissues%2F3028%23issuecomment-1347610815). Our solution to this was simply adding a "z" to the end of our health endpoint to make it usable.

####Multiple lines of businesses
The favorite service is one that has to be used across multiple lines of businesses which means generating a data schema that encompases all formats pertaining to a product or service. This challenge was tackled by maintaining close discussions and sync-ups with multiple teams to generate the most appropriate schema for all  product and services. This solution proactively considers any line of business that may be created in future.
Multiple lines of businesses also means a possible exponential increase in requests to favourite or unfavourite an item. This places emphasis on the criticality of service scaling based on the quantity of requests. GCR helps to achieve this because in Cloud Run, each revision is automatically scaled to the number of container instances needed to handle all incoming requests or events.

####Initial planning
Planning this project as a standalone service for multiple lines of businesses and probably data and investigation teams proved to be a slight headache especially concerning scaling and information provision. As mentioned earlier, it was critical to provide a service that maximises the use of functionality of the chosen tool while being cost effective. Using the bare bones of google cloud run wasn't enough because a new instance had to be spun up on the first request after service dormancy. This increases the time for the roundtrip of the first request. Thankfully the "minimum instance" option on deployments solves this because it leaves one instance running at a very low cost which is prepared to respond to any requests even after long periods of inactivity.

####Caching
The caching strategy was also a tricky one as data could be retrieved minimally or entirely. Minimally retrieving data refers to only obtaining IDs for a set of favorited items while retrieving data entirely refers to getting the entire object. The option to retrieve data minimally is provided because it can be used at the LOB(line of business) level to get a slightly faster result e.g. for display arrangement purposes, or before retrieving additional data. To tackle this challenge, queries with respect to a user had to be broken down into ones that requested for ids only and ones that requested for the entire data object before being set into redis. ID only queries are set with a special key (${userId}-IDS_ONLY) while entire object queries are set with the user ID only as the redis key.

To add to the complexity, the object stored for favorited items contains a freeform component which can also be used as query parameters on retrieval. Due to the inexhaustible combinations of unknown query parameters, it made sense not to cache queries for entire data objects with query parameters. Furthermore these queries do not happen often therefore the tradeoff on having them a little slower is satisfactory.

####Data loss
During development in the early stages the team realized the loss of data and tracking of unfavorited items since those are removed from the database. To tackle this challenge, an event logger was added to capture actions in connection to an item. This allows the service to keep track of items once the user interacts with them.


![Event Logger object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q66kc43tff9t3jm916t3.png)



###Conclusion
Generally projects like these are not set in stone. Changes based on improvements may be brought up during the projects lifecycle. This highlights the importance of close and consistent communication between all stakeholders through daily standups and meetings to alleviate the effects of these changes both on the team and on the project. Suggestion scrutiny, feasibility analysis, priority assessment and time management are all important contributions during sync-ups to help continuously improving the project.

If you found this article helpful and applicable for your organization, don't forget to click the 👏🏾 sign multiple times, leave a comment and share this article with your friends 🤟 See you in the next one! 😉