When building a mobile app that consumes web API’s, do you sometimes get the feeling that this requires way too much code on the client?
Does your mobile app performance suffer because you need to do multiple API requests and a lot of processing of API responses, before you can present the information that the user actually wants to see?
When you want to change or expand your app’s functionality, are you slowed down by a general-purpose API (team) that cannot match your speed?
In this post I will show how you can prevent these (and other) issues by designing an additional API specifically for each app frontend, using the BFF – Backend For Frontend technique. I will also show how you can consume a BFF API in a Xamarin app while minimizing client code and maximizing code reuse across clients and server.
This approach has an attractive cost/benefits ratio; I consider a BFF API to be almost a standard component of any mobile app that accesses nontrivial amounts of server data. Irrespective of which technology the generic API’s are implemented in, you can optimize agility and reuse skills and code by building the BFF APIs with the same team and the same technology as the Xamarin clients: with C# and .NET.
You can implement the BFF API in the same IT infrastructure as the existing generic API’s, if that infrastructure is agile enough for your needs and supports Microsoft technologies. However, the Microsoft Azure cloud with its App Service is also a good candidate to quickly implement scalable and reliable BFF API’s in C#, even if the generic API’s are on-premises and/or in an environment that does not support Microsoft technologies.
When you use the BFF approach, an app consists of more than just the iOS/Android/Windows frontend(s); the corresponding BFF API’s are part of the app solution and are built by the app team. Here is an example of an app in the context of an enterprise architecture:
Backend For Frontend
The term BFF – Backend For Frontend was coined by ThoughtWorks for a pattern that was used by SoundCloud during a project to move their system from a monolithic application towards an ecosystem of microservices.
The difference between using a general purpose API for mobile versus adding a BFF API for mobile can be illustrated like this:
The BFF pattern offers advantages for the overall architecture as well as for the app. Key advantages of BFF API’s (when compared to general purpose API’s) for the app users & developers are:
- Better app performance: no more than one request + response is needed for one user interaction, the payload contains only the data that is needed, and only minimal processing is required on the client.
- Better app maintainability: data models and logic (service orchestration, data transformation) are moved from client to server, where they require less effort and less time to maintain (e.g. no app store approval needed to deploy)
- More secure: the BFF API only exposes what is needed by the frontend, minimizing the API attack surface
For more on BFF, see this article by Sam Newman (also of ThoughtWorks).
Designing a BFF API
The primary design goal of a BFF is to provide a specific frontend with exactly the information and operations that it needs, when it needs it – i.e. the API operations and models should match user interactions. So to design the BFF API, you first need to have the UX – User Experience design. At least the wireframes of all app screens, with for each screen:
- All information that can be shown in the screen – all fields
- All actions that the user can perform on the screen that modify information, and which of those actions will be available offline
This is important – do not design (let alone build) the app API before you have the UX Design. Otherwise, you will be creating a general purpose API and not a BFF API, which would defeat the entire purpose of this component.
This also answers the question: how many BFFs do I need? As Sam Newman indicates in the above article: “one experience, one BFF. So if the iOS and Android experiences are very similar, then it is easier to justify having a single BFF. If however they diverge greatly, then having separate BFFs makes more sense”. Another example where you may need separate BFFs is when the tablet experience diverges greatly from the phone experience, or when you also support wearables.
When developing mobile apps with Xamarin, the experience is often very similar across platforms within the same class of devices (phones, tablets, wearables, TVs) – at least in terms of what information is shown and what actions are available on each screen. So typically you use the same BFF for iOS, Android and Windows clients of the same device class.
BFF API Design Artifacts
Since the BFF API will be designed, implemented and consumed by the same app team, we can often dispense with API design tools (such as apiary) and simply communicate our API design through code.
Typically, each screen from the UX design is translated to an API model which contains nothing but properties for all information shown. This is a serializable partial class, so we can extend this class in the client project to transform it into a viewmodel in which the model properties will be made data bindable (see “Consuming the BFF API” below).
Each action will be mapped to a method in the API interface.
Finally, it is beneficial to provide an implementation of the API interface that returns real-world design data, preferably with texts values of the maximum length that is to be expected in the real world. The design data serves both as additional API specification and as validation for the UI implementation – to see whether real-world data fits each screen comfortably.
Here is a minimal example with just one API model – Contact and two API actions – Get Contact and Save Contact:
This code file is shared as-is across server and client projects.
Consuming the BFF API
After you have designed the BFF API, you can start building the clients – do not start building clients before you have the API design and the UX design. You need both the viewmodels and the UX design to implement the UI efficiently. Remember that the API design includes real-world design data.
To transform the API model partial class into a working viewmodel with minimal code, we need to add three things:
- Add common viewmodel functionality: add a viewmodel base class to the API model.
- Add commands to invoke actions: add command properties and corresponding methods to implement the commands – e.g. for Save Contact
- Make the properties data-bindable. We use the Fody PropertyChanged assembly weaver for this, simply add the [ImplementPropertyChanged] attribute to the partial class
Here is the client code that does this for the above example:
Why use partial classes?
When you look at the example code, you may wonder why I use partial classes to extend the API model into a viewmodel. Wouldn’t it be more appropriate to derive the viewmodel class from the API model class instead? That way you could also put the viewmodels in their own namespace. The reason I do not use inheritance here is that it would break data binding for calculated properties in Fody PropertyChanged; Fody PropertyChanged not only makes properties data bindable, but also automatically injects code to make calculated properties data bindable. E.g.:
However, this does not work for properties in derived classes that depend on properties in base classes (see here for a discussion). Since automatically data-binding calculated properties is a valuable time-saver and prevents manual coding errors, I sacrificed a bit of coding righteousness to keep it working.
What about offline functionality?
Ok, so now we have an API that basically puts out viewmodels, and the code for data models and for their transformation to viewmodels is in the server and not in the client. But what if we have actions that change more than just the current viewmodel and we want to support those actions while offline? Wouldn’t you need to have data models and transformation logic on the client then?
The answer is simple: yes, you do. This is not an all-or-nothing approach. Only for these cases you can expose (just the necessary) data models in the API, and have the transformation code from data models to viewmodels in the client. Typically, this will only apply to a small part of the API, so the benefits for the client are preserved. Even if you want to add offline support later on, this is straightforward: simply copy the data model definition to the shared API code and move the existing (data model to viewmodel) transformation code from the server to the client.
Native mobile apps that access non-trivial amounts of server data can have better performance, maintainability and security by creating a BFF API as part of the app solution, with the same team that builds the apps.
Native mobile apps for iOS, Android and Windows that are built with Microsoft technology benefit even more, because they can reuse C# + .NET skills and code across clients and server. The key technology that enables this is Xamarin, which is now part of Microsoft.
Applying the BFF pattern effectively requires that you start with the UX design, then create the BFF API design including real-world design data, and only from that shared contract start building the API and the clients (which can be done in parallel).
Finally, it is possible to use the same C# source files (containing the viewmodel data definitions) across clients and server, by using partial C# classes and an assembly weaver for generating data binding code.