What are Microservices?
Microservices expose business capabilities in a loosely coupled fashion. Microservices are designed to be independently deployable, which also makes them independently scalable and (ex-)changeable. This requires them to communicate via message-based remote APIs as they encapsulate their own state.
Microservices architectures can be seen as having evolved from previous incarnations of Service-Oriented Architectures (SOAs). They consist of autonomous services that implement single business capabilities, often visualized as hexagons. These services are typically deployed in virtualization containers (such as Docker) that are clustered (e.g., by using Kubernetes). Additional tenets are polyglot programming and persistence, decentralized continuous delivery, and end-to-end monitoring as a DevOps practice (Zimmermann (2017)).
The benefits of microservices are:
- They support agile software development practices with continuous delivery. For example, each microservice is owned by one and only one team, which allows each team to independently develop, deploy and operate their microservices.
- They are well suited for implementing ‘IDEAL’ cloud-native applications (i.e., isolated state, distribution, elasticity, automated management, loose coupling).1 When deployed independently, horizontal on-demand scalability can be achieved through container virtualization and elastic load balancing.
- They allow an incremental migration of monolithic applications, which reduces the risk of failure of software modernization efforts.
Microservices also bring new challenges that need to be addressed. Their distributed, loosely-coupled nature requires carefully designed message-based remote communication and comprehensive systems management:
- The communication overhead within a distributed architecture combined with poor API design choices can impact the performance of microservice architectures.
- Scaling the architecture to include a large number of microservices requires a disciplined approach to their life cycle management, monitoring, and debugging.
- Single points of failure and cascading failure proliferation effects need to be avoided, for example through redundancy or circuit breakers. This reduces the risk that failing downstream microservice instances bring down upstream ones (and, eventually, the entire system).
- Data consistency and state management challenges are introduced, for example when decoupling monolithic, stateful applications into independent, autonomous microservices (Furda et al. (2018)).
- Autonomy and consistency for the whole microservice architecture cannot be both guaranteed when employing a traditional backup and disaster recovery strategy (Pardon, Pautasso, and Zimmermann (2018)).
A two-part interview “Microservices in Practice” that two of us conducted with James Lewis, Mike Amundsen and Nicolai Josuttis for IEEE Software (Pautasso et al. (2017a), Pautasso et al. (2017b)) clarifies terminology and discusses service design issues and additional challenges.
This blog post continues the discussion.
Why do we use Patterns?
Patterns are general, reusable solutions to commonly occurring problems. They are not finished designs, but outline how to solve a particular problem. A curated definition (based on the original from C. Alexander) is given by the Hillside group: “Each pattern is a three-part rule, which expresses a relation between a certain context, a certain system of forces which occurs repeatedly in that context, and a certain software configuration which allows these forces to resolve themselves.”
Some of the reasons we chose the pattern format to share architectural knowledge about API design include:
- Pattern names aim at forming a domain vocabulary, an ubiquitous language.
- Patterns follow a structured, recognizable text template.
- Patterns are soft around their edges as they only sketch solutions (and do not provide blueprints or templates to be followed blindly).
- Patterns are mined from practical experience (not invented) and then curated and hardened via peer feedback given in writers’ workshops.
- Solution sketches visualize the proposed design (in notations such as, UML, DSLs, or icon languages; informal rich picture are common too).
- Pattern languages define pattern relations and provide support for criteria-based decision making.
A pattern author is a journalist/reporter, not an inventor/designer, first and foremost. If you would like to read about our pattern journey so far, a personal retrospective (as of 2020) is available at Olaf Zimmermann’s blog.
What is MAP?
Microservice API Patterns (MAP) focuses on the design and evolution of Web APIs, emphasizing a novel perspective in the domain of pattern languages. MAP is concerned with the payload representations, i.e., the message content exchanged between API service providers and API clients or service consumers. The MAP patterns capture proven solutions to recurring API design problems, supporting decision-making during API specification and implementation activities.
MAP concentrates on the following aspects:
- The structure of messages and the message elements that play critical roles in the design of APIs.
- The roles and responsibilities of API calls.
- The impact of message content on the quality of the API.
- API descriptions as a means for API governance and evolution over time.
Our patterns are applicable not only to microservice APIs, but also to any remote API leveraging plain document messages rather than stateful protocols or remote objects, synchronous ones using direct HTTP exchanges as well as asynchronous ones based on message queues.
We do not cover integration styles and infrastructure architectures as other pattern languages do so already. For example, refer to the websites supporting the books on Enterprise Integration Patterns and Cloud Computing Patterns. We only touch upon protocol specifics in our examples and implementations hints; the recipes in Allamaraju (2010) provide detailed advice regarding RESTful HTTP.
See our Introduction to MAP article for a more elaborate description of the MAP project.