Director of Avenga Labs
Modern advanced apps utilize the power of the browser run-time using local storages, enabling more efficient network usage or even give the ability to work offline.
Android and iOS applications for phones, tablets and devices very often have different UIs than web pages and are optimized for mobile usage. For instance, banking applications usually start with the simple information about a default account and then options to perform various operations on the account, while web applications welcome users with more information up front about their accounts and other financial services offered by the bank. They also call on APIs to get that information, but in different forms and using different conversation patterns.
Intelligent speakers, car applications and a myriad of other connected devices also would like to enhance the user’s experience and in order to do that they need to use APIs.
They are often the largest number of consumers and are in large enterprises where the API can be accessed by corporate API brokers using queues and intermediaries. API can be used for detecting state changes (pooling), publishing, and receiving business events on the application or service bus. API calls can be orchestrated or choreography may be used. We may have direct HTTP calls by using pub/sub patterns with a messaging backbone.
Generic API is а one-size-fits-all, trying hard to accommodate the needs of every consumer. Good design conforms with Domain Driven Design and it attempts to anticipate business processes and use cases, which turn into conversations between different APIs.
A generic API is not aware of its consumers, which from the dependency standpoint is good.
Easy to enhance when application functionality is added and there is one single place to make modifications and publish new API contracts.
From the separation of concerns perspective, it is also great. API consumers depend on the generic API but the generic API does not depend on the way the consumer uses it. Another client may connect tomorrow and no changes in the API are required because of that.
If one solution is supposed to fit all the needs, it usually means it is not optimized for any. Multiple clients may suffer because of too many API calls to fulfill their UI requirements.
It’s crucial to explain this in more detail, so I’ll use examples.
Imagine a simple banking app on a smartwatch. By default, all it does is to display the amount of money available on the default banking account. The generic API returns a list of accounts with their details (which are not needed by the watch app), then the consumer has to make another call to get specific details of the account, including its current amount. As you can see, instead of one call returning a single number, we have to make two calls returning 90% of the data we don’t need for our client.
Another example is the reverse situation. An API is returning subsets of data that we need on the user-facing form and we have to make an API call for the list of items (like banking transactions to get their IDs) and then tens or hundreds of callsto get the details of each; latency is high and the user has to wait.
This is typical for so-called DB facades; you get a list of IDs and then you hit another API tens or hundreds of times to get the data you need. But that’s the price of something that attempts to fit all.
Remember – we are not supposed to touch the generic corporate API. The API architects don’t want to break anything as they want stability and to remain cautious. Technically it works. It’s not effective, but it works.
In this approach each client has its own API which fits their needs 1 to 1, so we have multiple APIs for multiple client applications.
The client gets exactly what they want in single API calls (usually per screen or fragment of UI); the latency is low (single call) and the data bandwidth usage is optimal as well (exactly what we need for UI, not too much, not too little).
The API is specific for a current UI flow, but not suitable for other clients. Using our examples, it’s good for desktop browsers but still not good for mobile apps. The number of client-facing APIs to be created is often underestimated; for instance, one for web apps, one for smartphones, and one for watch.
No matter if the business logic change applies to generic API nor the BFF – they have to be modified.
Both generic API and BFF are justified and make sense. Don’t they? Is there a way to benefit from both? Imagine working with a large enterprise application ecosystem with hundreds of business applications and API consumers, along with strong governance of an API management team, but a reluctance to make changes in order to preserve the stability of the API.
You can still create a backend for your front end. How? Many problems in software design can and are solved by adding another abstraction layer.
The generic API can be connected to the backend service that will act as a proxy for the API adapter and create your client-specific API. Avenga’s Couper.IO can help you do that effectively.
You can download tools from the product website, define mappings and translations between generic APIs, which you cannot touch to create your specific BFF API which suits the needs of your front-end application.
But don’t worry. It’s not another monstrous engine. It’s fast and has been proven in real projects connecting hundreds of modern, less than modern, and legacy APIs with an excellent UX, which is required.
In the discussion between the advantages of generic and universal API, and the needs of modern UX which expects a specific BFF, we should not forget that we can have both. Using a smart architectural design supported by the right product, it can be relatively easy to accomplish.
This enables you to create modern User Experiences while preserving existing (generic) APIs and thus accelerating your digital transformation.