Adapter Technique for BFF

Backend For Frontend: Two techniques and one to avoid

When developing a new frontend application for mobile or web, you will often find yourself needing to add a backend to support your development needs. Reasons for this could be that the Domain APIs that your client side application needs to communicate with are not publicly accessible (i.e. in an internal VPC) or you need to handle session management and authentication in a different way. Sometimes there is sensitive data in those calls that you may need to remove or orchestration between the downstream calls. Finally the decision even might not be a technical one but a skills based one, to capitalize on your teams capabilities and strengths.

Phil Calçado has a great resource to describe the strength and weakness of the Backend For Frontend (BFF) pattern, but if you do decide to build one, it is important to understand what approach you are going to take and the pro’s and con’s of each. Lets go over a couple of designs that I have used successfully in the past at a high level, starting with the simplest example where there is no Backend For Frontend involved at all.

This is the simplest and cleanest solution, but as stated before, is not always achievable as it requires the Domain API (s) to have the right accessibility (Public APIs) and compatible authentication mechanisms. Additionally if the people building the solution aren’t comfortable with building complex applications that transpile to JavaScript, you might want to put more of the business logic in a technology or language that suits your situation. If this is an option, you can host your static client side code on Amazon’s S3 or similar and reap the benefits of high availability and lower operational costs.

With this first technique, a very thin proxy layer is introduced to augment the existing Domain API call to suit your situation. For instance, the BFF might be deployed in a DMZ zone that has public access but also access into your data center or VPC. You might include session management and authentication before calling the downstream systems. The objective of this layer however is not to translate the behavior or data and simply allow it to pass through unhindered.

If your team isn’t comfortable with writing a scalable backend, there are a few off the shelf products that can help with this objective like MuleSoft, Axway or even the AWS API Gateway (these each have their own drawbacks). A downside to this approach can be that it can be difficult to discern some responses (401, 5xx etc) from the Domain API from the BFF without access to the error payloads or a sufficient amount of logging.

This is probably the traditional BFF approach where the heavy lifting of business logic and transformation is done in the backend, with the client side code being as simple as possible. This technique is also useful for predominantly backend teams that are responsible for a few front end pages. The Translation Layer being here also provides the ability to easily filter data so that unnecessary information isn’t accessible to the client.

One downside is that the BFF response codes are binary (success or unexpected failure) access logs and response code metrics will be less valuable. However the HTTP call to the Domain APIs should be instrumented with metrics and your application should be logging helpful application logs. Additionally, it does help accelerate troubleshooting because any non 200 response indicates the issue is with the BFF and not the Domain API.

This is the technique that generally you will want to avoid, it encompasses both of the previous solutions which results in duplication of business logic and unclear responsibilities from the BFF. Generally this approach treats the BFF as a mini or replica Domain API, returning generalised and RESTful standard responses, even though there is ever one very specialized consumer.

In addition to the transformation logic being duplicated in the BFF and Client Side, it also needs a third translation from the internal BFF model back into a REST model. Potentially this could be a solid approach if you had multiple different clients (say a native mobile app) that all need similar benefits that the BFF is providing, however I have not seen one of these scenarios and it might be better to enhance the Domain API to have that functionality directly.

New Requirement Scenario

The UI Component and Domain API will be consistent for all the different techniques, so let’s take a quick at what these could hypothetically look like. For the Domain API for simplicity will return just two responses. Later we will add a third response and compare the work required for each technique to support it. a 200 for a success, and a 204 for when no results exist. It can also return 5xx codes when something unexpected happens. A 200 response could look like:

{ "name": { "first": "Jane", "initial": "K", "last": "Doe"}, "locale": "en-AU", "other" : { ... } }

Next we will look at the data model the UI expects, again this is an overly exaggerated simple example, but assume there is some business logic into formulating the full name of a customer based on their locale (e.g. some cultures are Last First, others are First Last).

interface Customer { fullName: string }
interface UiState { customer?: Customer, serverError: boolean }

Now hypothetically suppose we need to add a new response from the Domain API, a 400 Bad Request for an invalid call and the appropriate changes to the UI Components new validationError boolean. What does that look like for each of the techniques? For the No BFF and Proxy solutions, they simply can add to the Client Side Domain Client code a mapping for the 400 response code, and in the translation layer return a UiState of { validationError: true }

Likewise for the Adapter BFF technique, the BFF Domain Client can handle the new response code as a VALIDATION_ERROR and in the translation layer include the validationError true boolean. No changes are required in the BFF resource layer as it will return the adapted domain model straight through. Neither are any changes required in the Client Side BFF client as it will pass the response straight through unchanged to the UI component.

Finally, the Duplicate approach would require the same Bad Request mapping in the Client Side and BFF client layers, as well as additional work in the BFF resource to un-translate the VALIDATION_ERROR back into a 400 response code to pass to the client side. This adds a lot more complexity to the solution which will slow down the rate new features can be added, the amount of tests needed and increases the chance of translation errors.

Final Thoughts

The Backend for Frontend pattern is invaluable to help organize microservice architectures and help coordinate functionality for front-ends. It can be simply an auxiliary layer between the user experience and the resources it depends on to remove network or security concerns. Alternatively it can be used as a layer to translate requests to the generalized domain layer below it, and translate the responses to the data model required by the UI. Whatever approach you decide, it’s important to avoid the pitfall to think of a BFF the same way one would think of an all inclusive microservice API, they are different and need to be coded as such.

I certainly don’t have all the answers and would love to hear what you think is effective when building a BFF.

About the Author

Mannan

Mannan is a software engineering enthusiast and has been madly coding since 2002. When he isn't coding he loves to travel, so you will find both of these topics on this blog.

Leave a Reply

Your email address will not be published. Required fields are marked *