By Marco Fioretti
Our previously published introduction to microservices outlined the key role that they play in the software infrastructure of more and more enterprises. In that article we also pointed out that microservices are not Application Programming Interfaces (APIs) but, as almost any other software, must use them. This time, we will introduce the main characteristics of APIs for microservices, and the corresponding best practices.
APIs for microservices are special
Public APIs are general purpose interfaces (e.g. for IP networking) that, at least in principle, could or should be usable and accessible to everybody from multinational companies to single programmers. Private or internal APIs, instead, would be those used, and usable, only by one company or software product.
Depending on its place in the system, a microservice may have to use any combination of those two categories of APIs. In both cases, however, those APIs would face specific challenges that should not be underestimated.
The first issue is security. Microservices live inside cloud-based, highly distributed, highly heterogeneous environments, scattered across many hosts. In such situations, “external” threats can and will appear everywhere. Even through APIs that would have been completely internal – that is (theoretically) safe from external attacks – in monolithic implementations of the same application, where they would have been calls between functions inside the same process.
This very nature of microservices and the common expectation to scale rapidly according to business demands, has deep implications for overall performances. The components of a monolithic program typically run as parts of one, or a few processes all inside the same host, or in the slowest case on the same LAN; they can freely use APIs to exchange information and data even hundreds of times in just a few milliseconds. In a microservice implementation of the same application, many of those same internal calls would become tens of API requests to other microservices, often across the Internet, taking way too long to complete.
Best practices for microservice APIs
The conclusion is clear: microservice APIs must be designed starting from those specific constraints, and with more attention than what would suffice with monolithic platforms.
To begin with it is crucial to avoid preferences, never design APIs for the microservices of some platform by starting from the needs and features of just one or two of those microservices. Instead, design a single model for the whole platform that is reasonably complete yet open to future improvements.
Besides optimizing overall performances, this approach minimizes risks. Modern business often expects that all the microservices of a certain platform are developed in parallel, but working in that way is riskier – with higher probabilities of troubles in the long run – than when done with monolithic applications.
Second, API design should really minimize the number of transactions that microservices engage in through their APIs – the amount of messages necessary to close each of those transactions, and their average size. The same applies to APIs redesign when migrating from an existing monolithic platform.
Third, give your microservices flexible communication points that can send or receive different types of messages. And give those messages – or more exactly to your whole APIs – explicit version numbers that keep everything backward compatible because new versions only add, and rarely change or dismiss features.
This is the good old “robustness principle”, whose next level with microservices consists of making their APIs “hypermedia-driven”. This term indicates APIs that include the machine-to-machine equivalents of web links, menus and forms. This makes it possible to exchange metadata that describe which actions, or “API affordances” are possible. In this way, if a “server” microservice is forced to change something in its messages, its “clients” can discover the change, and adapt to it, without being rewritten.
One gateway to serve them all
In addition to all the practices described so far, another tool that can greatly help a microservice platform is a “API Gateway”, which is a single entry point for all the external requests to all the microservices of a given platform. When those requests arrive, the gateway can split them into simpler requests for different microservices or aggregate all their answers into one message and forward that only to the original caller, with great advantages for all the parties involved.
To begin with, the API Gateway could improve security by reducing the attack surface of the platform and centralizing authentication and access permissions instead of replicating them in many different places. With adequate connectivity to all its microservices, a gateway could increase overall performance, especially with mobile clients. With a gateway, such clients could make only one slow request to one place, rather than requesting several microservices every time, potentially on different hosts.
Equally important is the capability of API gateways to leave their microservices free to evolve as necessary. A platform made of many microservices gives its developers the advantages described in our previous article. For its clients, however, the same fragmentation can be a problem if it means that they must know how to split what could have been a single API into many smaller interfaces.
Placing an API Gateway between external clients and internal microservices solves this problem, both in the present and in the future. The whole point of using microservices may be described as being free to change at any moment their configuration, internal communication channels, or even their physical distribution across private or public clouds with as little trouble as possible.
But this is only possible, or at least much easier, if there is a gateway in the middle that always exposes to the public the same interface and services, no matter what happens behind the scenes.