How to build a Platform that Churns Aggregators

airasia super app
AirAsia MOVE Tech Blog
4 min readFeb 15, 2021

By Manoj Gundluru

In the micro front-end world, data for a given consumer has a dependency on several upstream data feeds. To overcome network chatter of multiple calls we use an aggregator layer to help aggregate the service response from several upstream services.

What if you need to repeat this solution time and again for several micro front-end consumers?

Here is a case study demonstrating the platform thinking approach and converting a use case from building an aggregator to building a platform to churn aggregators.

This platform has helped us reduce time to market. A process that previously would have taken us a week or two, completing the aggregator now takes a few hours.

When I first got the task to create an aggregator service, I saw this problem as nothing but making parallel API calls and combining the results from each API, and sending back the aggregated results to the client. Believe me, that’s what I did for my first aggregator service.

Many teams were building aggregators and I was tasked yet again to build another aggregator a few weeks out.

“Wouldn’t it be amazing if there was a framework that only required us to configure the different endpoints and it will aggregate the results by making all the calls in parallel using the configuration?”

Building this kind of framework would be easy if all requests were similar. However the complexity increases with more granular requirements.

There are some challenges in building an aggregator framework:

  • Services need distinct access policies. A few services may need authentication
  • Not all tasks of aggregator are similar. For example: Get API call, Database Call, etc.
  • Some tasks may need additional behaviors. Ex: some API calls need a retry on failures.
  • Some tasks may be dependent on another task for execution.

It might be overwhelming to see all the challenges and come up with a design, so let us break the problem down into small parts and try to solve each separately.

I am gonna use java but a similar platform can be built using any object-oriented programming language.

Running tasks in parallel with configuration:

We need a runner class that will run all the given tasks in parallel but before that, we need to design how each task should look like.

Since all the tasks can be different, we need an Interface called Task with a method execute, we can extend this to create HTTP and another task:

public abstract class Task {private String response;private String name:void execute();}

We can use the response String to store the response in serialized form. Aggregator class implementation is nothing but an implementation of a function that will accept a List of Tasks and execute them in parallel, so am not gonna implement it here as it is straightforward.

Let me write a Task that does GET HTTP call for you to better understand the concept, you can observe that we are setting the result of the Task in the response variable of the class:

public class GetTask extends Task {
public GetTask(String name, String url, RestTemplate restTemplate) {
this.name = name;
this.url = url;
this.restTemplate = restTemplate;
}
@Override
public void execute() {
HttpEntity < String > entity = new HttpEntity < > (this.getHeaders());
try {
ResponseEntity < String > body = this.restTemplate.exchange(this.getUrl(), HttpMethod.GET, entity, String.class,
"");
this.setResponse(body);
} catch (Exception e) {
log.error(e.toString());
}
}
}

Now to make aggregator GET calls you just need to configure the below:

Task task1 = new GetTask(“task1”,”http://url1.com”,restTemplateInstance);Task task2 = new GetTask(“task1”,”http://url2.com”,restTemplateInstance);Task task3 = new GetTask(“task1”,”http://url.com”,restTemplateInstance);List<Task> tasks = new ArrayList();tasks.add(task1);tasks.add(task2);tasks.add(task3);aggregator.run(tasks)

Similarly, we can design different tasks by extending the Task class. Now we have a working aggregator system, but it is missing one crucial part which is adding behavior to the task. Behavior can be anything that can be applied to the task.

Examples of behavior:

  • Serving cached data
  • Retrying
  • Getting JWT token and adding it in headers

I like to call these behaviors as policies. Let’s assume you have to serve the data from the cache if it is available, you may think of implementing another Task and creating the execute method to add a caching behavior, but you may need to do the same for all the Tasks(GetTask, PostTask) you have created.

Now let’s take a step back and see the problem, we have to inject our behavior to any type of Task at the runtime when it is executing in the aggregator. Is there any design pattern that will solve this problem? You guessed it right, the decorator pattern.

The decorator pattern allows a user to add new functionality to an existing object without altering its structure

Now let’s design a Policy Decorator which will wrap Task execution with the behavior.

public class LogTime extends Task {
private final Task task;
public LogTime(Task task) {
super(task);
this.task = task;
}
@Override
public void execute() {
if (cache.ispresent())
Return data;
setResponse(data)
Else
task.execute
}
}

In the above example, we have created a caching policy that will take any Task as input and add caching behavior.

Now that we have a working aggregator that satisfies the requirements, let us create a platform using this simple solution. If we want to build an aggregator platform that can be consumed by multiple teams then we need to decouple the policies and core. If you think about one use case where team A uses Redis as caching but team B uses in-memory caching, then each team should implement their policies and inject them at the run time.

Based on the last observation we need a repository with all the core and common implementation of runner and Tasks but the policies should be developed by each aggregator based on their requirements. If you have common policies in your organization then you can create a library of extensions so that it can be reused by different aggregator implementations.

Hope this is helpful, happy coding :)

--

--

airasia super app
AirAsia MOVE Tech Blog

The product and people stories behind ASEAN’s fastest growing travel and lifestyle super app.