BFF layer: When it’s more than just an API gateway | by Abhilash Jain | Medium
BFF Layer is basically an orchestrator layer which is also called as Backend for frontend. When an orchestrator layer includes aggregation, computation, composition of some data then it’s more than a simple API gateway. Owing to the fact that most of the companies are moving to micro-service architecture for some obvious reasons (scalability, development cost, maintainability etc). As a result of which, it becomes difficult for every micro-service to provide an exact API to each of its clients considering each client has different specs & different data requirement.
There are confusions around BFF layer, sometimes BFF is generally confused with some of the gateway pattern which are as follows:
- Gateway routing pattern
- Gateway offloading pattern
- Gateway aggregation pattern
Irrespective of the fact, BFF is mostly used to support it’s client request with one-size-fit all API and it also includes late two patterns. Below snapshot clearly depicts how an ideal BFF layer looks like.
How do the clients (web, mobile, others) access the services?
- Each client has different requirement which might not be possible for each micro-services to provide the exact data in one API. e.g. web client needs more data means more payload but this is not true in case of mobile. One can’t simply trade-off the latency in mobile especially when one of your product is being used in low resource network.
- Services might use different protocol and such complexity should be hidden from the client.
- One can never provide one-size-fits-all style API, so it has to be dependent on what client needs. e.g. Netflix has more than 800 device type, that’s why it came up with an approach of client specific adapter code.
Problems that you might face
So, now you have your micro-services lying beneath your BFF layer but still your BFF have one of the complex problem to be solved which is related to joining of data from different services e.g. listing page. Let’s see how can we solve this problem. Basically, there are two approaches:
API Composition Pattern
Suppose you have to show booking listing page and there are two micro-services, one takes care of booking and other takes care of hotels. We can easily join the data by hitting both the services, getting all of the data and joining it in in-memory.
This looks very simple, first you fetch all of the data from different services and join them. It works well unless you don’t have large data sets. It scales horribly on large data.
- Command Query Responsibility Segregation (CQRS) Pattern
In this case you need to maintain a separate database containing all your needed de-normalised data. You can sync it either by pull or push mechanism. Below is the snapshot where you get events from both services and store it in your service. It has two fold advantages:
Pagination problem get solved.
I/O time get reduced as we are making only one call to our Listing Service (fetching data from denormalised table).
What is expected from BFF layer?
- First and foremost, providing only the data what client needs, not more or not less. Large and unnecessary payload always adds up the latency.
Maximum throughput, # request per unit time. That truly depends upon the technology & the right approach being used. - Less response time. This can be done by asynchronously calling other services and some other slicing & dicing out.
- It should be resilient by design with proper implementation of circuit breakers, timeouts, retry etc. Reason being, it is a single point of failure for your client so it has to be thoroughly load tested.
- If one or more service is taking too much time, then it is okay to return partial result but not in the case of Listing page where we need to show all of the data.
- Use of caching would be good incase of failures. There are some data which need not to synced every seconds or minute. Find out what are those and cache it with proper TTL.
What tech stack should we use?
Well, we still don’t have a straightforward answer for this. It purely depends on lot of factors. One can simply search and see it’s a fight between Java and NodeJS.
It largely depends upon the tech stack of your company, what are the skill set you are adding up to your team, and most importantly what are we trying to optimise (development cost, performance, maintainability, memory etc etc). In a nutshell, I’ve listed some of the noted difference between the two.
NodeJS
- It operates on one main thread which uses background threads for performing tasks. So, you need not to worry about threading, locks & consistency of data since your main thread is changing all your data.
- Highly suitable when it is IO bound.
- You can expect faster development with less complexity.
Java
- It runs as a single process which uses threads. And, each request is handled by thread.
- Highly suitable when it is CPU bound.
- Since Java has 20 years of solid engineering so you can not ignore the power it gives when it comes to mathematical computation and others. It has got better IDE and remote debugging which makes life more easier.
- It is generally said that NodeJs is faster because it can do asynchronous operations easily but Java is also no less than that. With Spring reactor, one can actually do asynchronous and non-blocking things with correct server chosen (Ratpack, Tomcat NIO or other servers built on the top of NIO connector). Moreover, some group of developers from companies like Netflix, Pivotal, RedHat, etc. got together and converged on something called The Reactive Streams Specification and this form the basis of Project Reactor.
Just to give a vanilla flavour, above is the little snapshot of how Spring web-flux can actually do asynchronous and non-blocking things. The point is everything from the incoming request to the database fetches and till the request is dispatched, has to be non-blocking. I’ll be sharing the power of project reactor in my next blog!
Please do comment your approaches & suggestions if any, would greatly help us to evolve together.