Backend for frontend or BFF design pattern.
In the Backends for Frontends (BFF) pattern, a dedicated backend service is allocated for each distinct frontend or client type. This strategic approach enables the optimization of the backend to align precisely with the unique requirements and user experience specifications of each specific frontend interface. Implementing Backends for Frontends enhances overall system efficiency and responsiveness, ensuring tailored solutions for diverse user interactions.
Table of contents
Evolution of frontend systems
Once upon a time the only frontend use to be the web. Over time mobile apps became more popular. Today we have several frontends for different type of users and clients. For example, you may have android and ios apps, tv apps, voice assistant apps, car apps and also have web apps. Different engineers work with different technologies to make these clients work and their target demographics also is different.
Prior to this, you would often have a backend that was responsible for "business logic" and the clients interacted with it directly. In my case, I worked with a major ecommerce giant that had a pretty large backend that maintained APIs to list product and SKUs. We had an angular based frontend that fetched these and presented them in the browser. Another mobile team used the same APIs to display the same products in the app.
As time passed we wanted to perform experiments. Show different product titles, differnt images and also different kind of texts. This meant, somewhere in the backend logic we had to have if else blocks that would split the output. But what if I want to run an experiment only on mobile app but not on web ? We had to now modify the backend logic to factor in the client id.
All this was making things complex for backend developers. Since our core backend was the source of truth for all product information, touching it required rigorous testing and bugs there were very costly. Deployment took hours and we worked in long sprints.
But then due to business reasons we had to refactor our backend and a major problem arised. Our frontends were deployed on millions of mobile apps. They expected certain APIs. How are we going to modify our backend APIs if the clients rely on them ? It could take around year plus for all those mobile apps to update. Some can never update.
Backend for frontends came to rescue
We decided to build a layer called "orchestration". These array of servers did not do any business logic instead they simply reached out to our microservices which had the business logic. This business logic changes lot less frequently. The team could focus on just the core business logic instead of frontends.
The orchestration layer was responsible for talking to clients and would provide whatever the client expected. This was our backend for frontends. The interface between this BFF and backend could change as often as we wanted it to change and it was simple. We could rearchitect our backend to be more microservices oriented without having to worry about client interface.
Problems solved by Backend for Frontends.
The backend logic and APIs can change without impacting client.
Since clients do not talk to backends directly, you can keep changing your backends as you please. Client updates are expensive. Server updated are cheap. This means if you change your backend apis or introduce new microservices, you can simply change your backend for frontend logic without changing the APIs that face the app clients.
Source of truth business logic is separated for frontend logic
Business logic is often driven by business needs, laws and hard truths. For example an API that is supposed to return to you your bank balance must always return an amount which is accurate. Everyone using this API knows this expectation. However for say privacy reasons you might not want to render the amount on the screen, may be you want to show only last 3 digits as such.
If you do not have backend for frontend, where do you place this logic ? You might have to place it in the client code where you fetch the real amount but chose not to show it. This is unsafe and harder to update the logic. Also if you have 4 different type of frontends this logic gets repeated at 4 different places.
A backend for frontend might fetch you bank balance from the backend but then chose to render it differently to the client. This means your backend does not have to worry about what client is showing and the client apps can be really dumb and show whatever the API call returned.
This principle is sometimes called "separation of concerns".
Performance optimizations
Imagine a backend API that lists all product features. It could be too many and your mobile app may not even show all of them. The backend for frontend can prune that and present a limited features back to the mobile app, thus saving precious bandwidth. It can also resize images, cache frequently used data and such.
Often these backend to frontends often do very less computation intensive operations and are mostly fetching and combining data from other microservices. Hence they can run on cheaper and simpler hardware.
Arguments against frontends for backends.
This archiectural pattern is not for small project. It would add needless complexity to what is essentially simple. If you app is only web app then too you do not need this pattern and the web frontend code can be updated very easily.
There might be cases where data consistency across multiple apps might be legally required or a hard constraint. In such cases too you should be smart about thinking if you need BFFs.
Conclusion
Backend for frontend is a common architectural pattern that is often recommended for applications that have different type of frontend clients, high traffic and where costs of updating client code is high.