BFF — The secret of all great mobile architectures (Part II)

Viton
Wellhub Tech Team (formerly Gympass)
6 min readNov 3, 2020

--

If you have read Part I of this post you already know some issues we've had when requesting REST API’s to build our first app version. In summary, that’s what we wanted for our mobile client when requesting an API:

  • Abstract as much as possible business rules or calculus based on the payload
  • Do as few as possible requests to get screen information
  • Pieces of information there are more willing to changes (such as translations, company emails, and URLs) to be server-side

At the same time, we wanted our API’s with:

  • Solid interfaces
  • The same behavior for all it’s clients
  • REST principles

What our first version looked like:

What we wanted to achieve:

Okay, but how? This diagram doesn’t give us many details about the implementation and we needed to discuss a little bit more about some crucial points. Such as:

  • Is the BFF closest to the API’s or to the mobile client?
  • Which programming language are we going to use?
  • Does it need to have authorization?
  • Should it return already translated content to the client?
  • How is it going to be integrated with the current APIs?
  • Is it a proxy for other services? Does it have a database?

This post is to explain how we have found all the answers to these questions.

Ideally, the BFF was thought to solve the needs of our mobile client and to keep our API’s as REST as possible. It’s natural for us that the BFF is closest to the mobile app, as one of its resources.

So, the synergy between the mobile app and the BFF would come in many ways, and the programming language is one of them. As we spoke in another post we do use ReactNative as our native development framework. With that in mind, Javascript became candidate number one.

At the same time, the most common problem when requesting a mobile application API is the interface changes that break the app and could start several crashes in your app. So to move forward with Javascript it would be nice to guarantee a stronger defined interface, with primitive types. And that was our first shot: a POC (proof of concept) to be written with Typescript. We did it using a known feature: Check-in.

To build and create a new solution it shouldn't be exhaustive. Something that has helped a lot was to schedule 30 minutes meetings every day, so we could share our last findings and adjust the next things to be discovered.

After both POC’s we knew that GraphQL would give us the strong API interfaces we expected without having to adopt Typescript, with a much more familiar way and less painful. Currently, we’ve been working with Apollo GraphQL as the desired framework.

Authorization

All of our mobile requests should be valid requests (authorized), previously authenticated. This is a spec for all our services. On the other hand, to build such a feature in all our microservices that will come next (we have started to move for a microservice approach at the same time) is an exhaust boilerplate. Our next challenge was set: how to navigate user’s requests safely without having the authorization boilerplate? Or maybe, how to reuse authorization for all our APIs?

A brief parenthesis about our first feature with GraphQL: the Home page. For its first implementation, the home page just needed some user's basic information. At that moment our API in our monolith application had a very slow response time. Some colleagues decided to create our first read-only service, where the goal is to be filled through events to update and provide a very fast API for reading a resource. It was faster than reading a JSON file published in AWS S3. This is our reading service responding to a request in less than 10ms.

If you know anything about AWS you probably have heard about API Gateways. This post explains a little bit more: [https://medium.com/faun/securing-api-gateway-with-lambda-authorizers-62845032bc7d]. We have decided that the BFF would be protected by an API Gateway, and it’s the Gateway that will be responsible for our authorization flow, allowing our requests to navigate through our services with network protection.

That’s how the authorization boilerplate should be. Imagina 10+ services with the same flow? Terrible!
And this is the solution we adopted. No authorization needed once the request is inside our ecosystem.

Our Home page just needed user information, and now we have a new service to provide it very fast. We don’t need any kind of persistence connected to the BFF, it’s just a requester for other API’s. As Bonus (or not) the BFF latency it’s defined by the API’s response time.

Translations

Gympass is presented in 14 countries, and translations are a concern when developing any feature. Our concern: where to put our translations? Deploying it with the application would mean that our messages could change only in the next release. To make a request for getting updated translations could lead to a bad user experience (for the same reason we have explained in PART I about nested HTTP requests). But the BFF rises as a good place for the translation and it could be simple, real-time updated, and scalable.

And how is it? GraphQL has a feature named Schema Directives: https://www.apollographql.com/docs/graphql-tools/schema-directives/. It allows us to respond to a value when resolving a field and after all the executions it does a new process depending on the directive you’re using. Let’s see a little more about it.

This is the API Interface we define server-side:

The notation @translate means that we want to call the translation directive after resolving this field.

To resolve this query we could just return this value.

When receiving “home.title” as a parameter the Translate Directive only needs to call the appropriate API with the language we want and get its value.

Layout Driven

After the translations process, we noticed that we could use BFF not only for transactional data (user data, check-in data, partners data) we needed but also for layout information (page titles, subtitles, images) also. That way, the relation App ←→ BFF has become much more layout-driven — “what I need to show on the page” — rather than returning the data to the app decide how to handle it.

For example, a list of a user’s favorites facilities in which this user could have added facilities or not. Normally we would ask for favorites and receive something like this:

And if there are no favorites for this user:

But as we have moved to a more layout based approach, what we really ask for the API is:

The empty case is that the favorites component shouldn’t be rendered at all.

This approach turned our app much more reliable, and we started to give a new “value” for the null reference. Null represents something that should not be rendered due to empty data, an API error, an A/B test, or any other reason.

Important: If you’re reading the term BFF for the first time you should know that it is not a pattern we invented, and every BFF has a different implementation depending on the product and the clients. This is a good reference for this pattern: https://samnewman.io/patterns/architectural/bff/. Soundcloud also has a nice post about BFF that we found (and liked) some time ago when it was just a dream for us: https://medium.com/featured-insights/bff-soundcloud-b37525f1e04.

--

--