Microservices Best Practices

API Gateway

API Gateway acts as a single entry point for all clients with a more critical role as an edge service for exposing microservices to the outside world as managed APIs. It sounds like a reverse proxy, but also has additional responsibilities like simple load-balancing, authentication & authorization, failure handling, auditing, protocol translations, and routing. There are several ways in which the API Gateway can be deployed from a development perspective.

API Gateway Style Microservices Architecture (Image: Microsoft Azure Docs)

  • Build it programmatically — to have better customizations and control
  • Deploy an existing API gateway product, (such as Apigee) — to save initial development time and use advanced built-in features

Some design patterns that explain API Gateway behaviour are as follows (Read Design patterns for microservices).

  • Gateway Aggregation — aggregate multiple client requests (usually HTTP requests) targeting multiple internal microservices into a single client request, reducing chattiness and latency between consumers and services.
  • Gateway Offloading— enable individual microservices to offload their shared service functionality to the API gateway level. Such cross-cutting functionalities include authentication, authorization, service discovery, fault tolerance mechanisms, QoS, load balancing, logging, analytics etc.
  • Gateway Routing (layered routing, usually HTTP requests)— route requests to the endpoints of internal microservices using a single endpoint, so that consumers don’t need to manage many separate endpoints

Note that an API Gateway should always be a highly-available and performant component since it is the entry point to the entire system.

Smart endpoints and dumb pipes

Each service owns a well-defined API for external communication. Avoid leaking implementation details. For communication, always use simple protocols such as REST over HTTP.

Asynchronous communication

When asynchronous communication is used across services, the data flow does not get blocked for other services.

Avoid coupling between services

Services should have loose coupling and high functional cohesion. The main causes of coupling include shared database schemas and rigid communication protocols.

Decentralize development

Avoid sharing codebases, data schemas, or development team members among multiple services/projects. Let developers focus on innovation and quality at the source.

Keep domain knowledge out of the gateway.

Let the gateway handle routing and cross-cutting concerns (authentication, SSL termination).

Token-based Authentication

Instead of implementing security components at each microservices level which is talking to a centralized/shared user repository and retrieve the authentication information, consider implementing authentication at API Gateway level with widely-used API security standards such as OAuth2 and OpenID Connect. After obtaining an auth token from the auth provider, it can be used to communicate with other microservices.

Microservice security with OAuth2 and OpenID Connect (Image: Kasun’s Blog)

Event-driven nature

Human beings are autonomous agents that can react to events. Can’t our systems be like that? (Read: Why Microservices Should Be Event-Driven: Autonomy vs Authority)

Fault tolerance

Since the system comprises of multiple services and middleware components, failures can take place somewhere very easily. Implementing patterns like circuit-breaking, bulkhead, retries, timeouts, fail fast, failover caching, rate limiters, load shedders in such vulnerable components can minimize the risks of major failures. (Read: Designing a Microservices Architecture for Failure)

Product engineering

Microservices will work well as long as it is engineered as a product, not as a project. It’s not about making it work somehow and delivering before the deadlines, but about a long term commitment of engineering excellence.

Content originally from Thilina Ashen Gamage