Category: Design Philosophy Tags: api-first, contract-first, taxonomy, composition, microservices, architecture
Use consistent terminology when discussing API types:
| Term | Definition |
|---|---|
| Platform API | An API exposed directly by a microservice. Represents self-contained business functionality. |
| Gateway API | A composed API that aggregates calls to two or more microservices. Lives in the API Gateway tier. |
| Application Specific API | An API tailored to a specific client application or partner. Not publicly documented. |
| Backend for Frontend (BFF) | An Application Specific API designed for a specific user-facing client (web, mobile, etc.). |
| Backend for Integration (BFI) | An Application Specific API designed for a specific B2B partner or integration. |
Preference order: Platform APIs > Gateway APIs > Application Specific APIs.
Platform APIs and Gateway APIs are publicly documented; third parties can build applications against them. Application Specific APIs are internal; they are not intended for third-party use.
API First means:
This philosophy was articulated by Jeff Bezos at Amazon: all teams must expose their data and functionality through service interfaces, and those interfaces must be designed as if they will be externalised to the outside world.
A well-designed API has a single, clear concern. APIs with many reasons to change are volatile and expensive to evolve. Prefer APIs where data and functionality are well-partitioned and distinct.
Design the API collaboratively with its consumers. The contract is a promise to clients; it must be agreed before implementation. Documentation must be complete enough for a client developer to build against the API before the server is implemented.
A well-documented, contract-first, well-partitioned API is also highly testable. Consumer Driven Contract tools (e.g. Pact) allow clients and servers to communicate their contractual obligations, enabling the contract to evolve safely.
Do not couple your API resources directly to your domain entities. Avoid serialising domain objects directly into request or response bodies.
Instead, use an anti-corruption layer:
This allows you to evolve the implementation without breaking downstream consumers.
┌──────────────────────────────────────────────────────┐
│ Microservice │
│ │
│ HTTP Request → [Anti-Corruption Layer] → Domain │
│ HTTP Response ← [Anti-Corruption Layer] ← Domain │
└──────────────────────────────────────────────────────┘
When a single request requires data or actions from multiple microservices, you have two strategies.
An API Gateway sits between the perimeter and your microservices. It mediates requests and can compose calls to multiple microservices into a single response.
graph LR
Client["External Client"] --> GW["API Gateway\n(Composed API)"]
GW --> MS1["Microservice A\n(Platform API)"]
GW --> MS2["Microservice B\n(Platform API)"]
GW --> MS3["Microservice C\n(Platform API)"]
GW --> MB["Message Broker"]
MB --> MS4["Microservice D"]
Benefits of Gateway composition:
Drawbacks of Gateway composition:
The client makes multiple direct calls to Platform APIs.
Benefits:
Drawbacks:
| Scenario | Recommendation |
|---|---|
| Reducing client round trips is critical (mobile/web) | Server-side composition (Gateway) |
| Composition logic is complex and multi-step | Server-side composition |
| A single client needs tailored orchestration | Application Specific API / BFF |
| Simple data joining across 2–3 services | Consider Event Carried State Transfer first, then client composition |
| Query requires combining data from >2 services at high frequency | Server-side composition |
An API Gateway is also a proxy. It can centralise these cross-cutting concerns so individual microservices do not need to implement them:
Centralising retry logic at the Gateway prevents “retry storms” where multiple services independently retry against a failing dependency.
An Application Specific API is tailored to a specific class of clients (a partner, a device type, etc.).
Primary advantage: The tailored solution fits client needs precisely.
Primary disadvantage: Proliferation of such APIs significantly increases maintenance cost.
Application Specific APIs:
/applications/{application} (see URI Design).When building a new capability that initially targets a single application: