Skip to content

Microservices

It is common to expose every service in an application through a JSON REST interface. That is not necessarily the wrong choice, but picking the right tool for each job leaves the application simpler, more efficient, more secure, and easier to monitor and maintain — a service whose job is to notify others of something that happened is usually better served by a message than by a synchronous call.

Designing a microservice architecture means setting aside some principles architects followed for a long time and applying others in their place. The patterns below relate to the size, lifecycle and behaviour of microservices and to the relations between them.

Microservice-based applications can be designed using Domain-Driven Design, an approach that requires a close understanding of the business domain. A domain can have multiple bounded contexts, each of which encapsulates the details of one business domain and defines the integration points with the others.

In an e-commerce application, order, delivery and billing are all separate bounded contexts. Each context maintains a data model derived from the bounded domain model, and each translates into one or more microservices.

Advantages of the bounded domain model:

  • Changes in the domain model affect only a limited number of services.
  • Services are autonomous.

Disadvantages:

  • Good domain knowledge is required to define a bounded context correctly.
  • Keeping the system consistent between contexts adds complexity.

Bounded Context and Aggregates cover how those boundaries are found.

Microservices are stateless and keep their state outside the application, in databases or data grids. That is what lets an instance be replaced, scaled out or deployed independently of the other services: any instance can serve any request, so adding and removing instances is a capacity decision rather than a data-migration exercise.

Distributed applications fail through code errors, hardware faults and network faults. Applications have to anticipate those failures and recover from them rather than assume they will not happen.

Several patterns make a microservice application fault-tolerant:

  • Run services highly available, so the loss of one instance is absorbed rather than visible.
  • Apply the circuit breaker pattern so a failing service or network path does not cascade into the services calling it.
  • Apply the fallback pattern so a failed call degrades the response instead of failing the request.

Microservices can communicate asynchronously using message-based protocols such as AMQP or MQTT, or by applying message-based patterns — point-to-point, publish-and-subscribe, request-and-reply, request-and-notification. Depending on the protocol or pattern, an intermediary such as a broker is responsible for delivering messages to the correct destination.