What Are Microservices?
Microservices, or more accurately microservices architecture, is first and foremost an architectural approach to building large-scale software systems. The primary goal revolves around dividing a system into many small independent components that have a single responsibility. Each component lives in its own environment and can be deployed, tested, scaled, and managed independently.
The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms- Martin FowlerSoftware Development Activist
Microservices architecture however is a multifaceted concept. It's not just an approach to system design but also a collection of tools, strategies, and methods that all contribute to improving functionality and flexibility. James Lewis says it best:
Microservices is a coming together of a bunch of better practices from a number of different communities.
- James LewisSoftware Development Activist
In this post I hope to provide a basic introduction to microservices architecture, explain what problems it can solve, and touch on a few best practices that accompany this approach to system design.
A Monolith in the Kitchen
In order to comprehend a microservices architecture it's helpful to understand how many of today's systems are structured and what issues they face. In order to do this, let's consider a popular kitchen appliance of the mid 1980s: the range-microwave all-in-one. In case you're not familiar, they looked like this:
It really is an exciting appliance. With a single purchase and delivery, you could own an oven, stove, microwave and even some ventilation. It's essentially your entire 'kitchen system' all in one unit!
We could call this sort of 'kitchen system' a monolithic system or simply a monolith. Its a single unit or thing that offers lots of functionality. Our monolithic kitchen offers three main functions provided by three main components: the oven component, stove component, and microwave component. Although you may be able to identify the individual components, they are still tightly integrated together as a single unit.
Benefits of the Monolithic Kitchen
- The components share a common structure, a single power source, common electrical wiring, and a single control panel
- You only need to buy, ship, install, and maintain one unit
- It's relatively efficient size-wise (compared to separate appliances)
- It all goes in one spot in the kitchen (separate appliances are usually installed in separate locations)
In a simple residential kitchen, the monolithic approach makes a lot of sense as it has relatively minimal overhead. It's also initially less expensive than buying separate appliances.
Many of today's software systems are constructed in a similar way. A suite of functionalities are often baked into a single unit or thing we like to call a application. This single monolithic system has benefits similar to those of the all-in-one kitchen unit. It only needs to be deployed once, it can benefit from shared wiring, and it all goes in one spot.
But, as you can probably guess, this approach definitely comes with drawbacks.
Where the Monolithic Kitchen Falls Apart
Now let's play the what if game with your Monolithic kitchen.
- the microwave stops working -> good luck replacing it
- the control panel dies
- you decide you want a gas stove instead of an electric one
- the oven isn't big enough for your Thanksgiving turkey
- you remodel the kitchen and want the oven and stove in separate places
- a small fire on the stove melts the electrical wiring
- you just want to upgrade your appliances but can't afford to do it all at once
The monolithic kitchen just isn't flexible or adaptable enough to practically handle these realistic scenarios. It's also very challenging to incrementally update. You might be thinking "maybe I should have bought separate appliances".
Monolithic software systems experience many of the same growing pains as the monolithic kitchen. New technologies become available but they're hard to integrate into the monolith (converting to a gas stove). The system experiences heavy load on certain components (the Thanksgiving turkey) and they need to be scaled to maintain performance. Errors or memory leaks in one component could affect the entire system (a stove fire melts the wiring). Replacing poorly performing components with new ones into the outdated framework of the monolith is challenging (installing a new microwave after the original one dies).
The Microservices Approach
Now let's say you purchased separate kitchen appliances instead of our beloved range-microwave all-in-one.
You might pay a higher cost initially but over time, you grant yourself much more flexibility with this approach. Microservices allow the system to incrementally evolve. This is paramount when it comes to enterprise software systems. It might be feasible to consider overhauling your kitchen and replacing all the components at once but, when you consider large software systems, total overhaul is rarely an option. It's often too expensive, time consuming, and risky to be a viable approach. Incrementally updating a system piece by piece over time is a much safer and more practical way to evolve.
Caveats of the Microservices Kitchen
It's not for everyone
There is no one-size-fits-all solution to software architecture. Determining whether a microservices approach or monolithic approach is right for you all depends on the particular system and the skill level of the team implementing it. Generally speaking, large complicated and evolving systems would benefit from a microservices approach where smaller more static systems might be better off sticking to a monolithic solution. Think of a residential kitchen vs. a school cafeteria kitchen. At home, the all-in-one unit might make more sense than buying individual appliances. It's certainly cheaper at the onset. But in a higher volume cafeteria, having separate appliances is a no brainer.
Purebreds are Rare
Few systems are purely monolithic or purely microservices. In practice, systems are usually some hybrid of the two. When you think about kitchens, the most common set up today is actually a microwave-vent combo and a stove-oven combo. It's a half-way point between monolithic kitchen and the microservices kitchen.
It adds complexity
At risk of over-simplifying things, I'm going to cautiously state that microservices offer more flexibility at the cost of increased complexity. I'll get into technical details later but generally speaking, managing a suite of multiple services requires more work than managing a single well-defined service. But when you're considering a complex system that grows and evolves over time, the added complexity of a microservices approach may be worth the additional effort. Martin Fowler refers to this concept as the "Microservices Premium" and stresses that your system essentially needs to be too big for a monolithic approach before considering a microservices architecture.
You must be this tall to use microservices.
- Martin FowlerSoftware Development Activist
3 Tenants of Microservices
1. Only Do ONE Thing
Microservices should be micro in size! A microservice must aim to achieve one fine grained business capability. Many of the benefits of the microservices approach depend on services remaining small, simple, and easily comprehendible. A service that can do multiple different tasks becomes too complex for a small team to manage and will have too many dependencies to truly be self-contained. As a service grows in size, releases take longer, changes become harder to implement, and the whole thing starts to become inflexible.
Microservices are a shared nothing architecture
- Neal FordApplication Architect at ThoughtWorks, Inc.
Microservices are ideally self-contained. They run in their own independent operating system process and contain all of their dependencies. They also have their own computing resources (memory, processing power etc). This allows microservices to be independently deployable and testable. The United States declared independence 1776. It's about time services do the same.
3. Smart Endpoints Dumb Pipes
Many monolithic systems employ a complex service bus to manages communication between system components. This adds complexity and prohibits a service from achieving independence. The microservices approach favors "dumb pipes" which means there is no logic included in the medium used to communicate between components. Instead, logic exists exclusively within the services (i.e. "smart endpoints"). Most microservices use simple HTTP messaging and REST APIs for communication.
A microservices architecture offers both technical and organizational benefits. When comparing microservices and monoliths it really boils down to a trade off between flexibility and complexity. A system of microservices offers much flexibility at the cost of increased complexity while a monolith is relatively inflexible but easier to stand up and get running.
Technical Benefits of Microservices
A monolithic approach is centered around a common set of technologies. Decisions about what languages, tools, and database platforms to use are made at the onset of system development. What if certain system components would benefit from using a different language? What if a new database storage solution becomes readily available and would significantly increase data access speeds? Once in place, the monolith becomes married to its defined technical stack. Integrating new or different technologies into a monolith requires overhauling large parts of the system. This becomes extremely complex, expensive and SLOW.
It's all about choosing the right tool for the job and leveraging it correctly.
- Neal FordApplication Architect at ThoughtWorks, Inc.
|Technical Stack determined upfront||Technical Stack determined over time|
|Technical Stack applies to all services in the monolith||Technical Stack is chosen on a service by service basis|
|Embracing new technologies is high risk, expensive, and slow because it affects multiple services||Embracing new technologies is lower risk, less expensive, and quicker because it affects only one service|
|Difficult for system to evolve technically :-( because of technical inflexibility||Easier for system to evolve technically :-) thanks to increased technical flexibility|
Microservices allow the independent service teams to determine the set of technologies that are most applicable to the unique problems they face. New services can use new technologies without having to convert / rewrite older services. This enables microservices to be more receptive to technological evolution and INCREMENTAL CHANGE. THIS IS HUGE!
Applications should be written in a mix of languages to take advantage of the fact that different languages are suitable for tackling different problems.
- Martin FowlerSoftware Development Activist
Consider a system where certain components experience more load than others. Take a bank for example. The Credit Card Transaction service will have orders of magnitude more traffic than the Credit Card Payment service. In a monolithic approach, the whole Credit Card system would need to be scaled in order to handle the load from the Credit Card Transaction service.
Now consider a microservices approach where individual components can be scaled based on their specific needs. This allows the bank to scale only the Credit Card Transaction service. Separating services into their own processes and scaling only the high load services promotes more efficient use of resources.
When systems are separated into independent services the risk involved in changing or upgrading a single service is reduced. In a monolith, an error, memory leak, or high processor usage introduced by one component could negatively affect or even bring down the entire system. If a microservice experiences an error, memory leak, or high processor usage the effects are confined to a single instance of the service and do not directly impact other services in the system.
When data stores are distributed and replicated as they are in a microservices architecture the system benefits from a reduced risk of data loss. Monolithic systems however, are more likely to have single point of failure concerns around their data storage approach.
In the monolith, components interact with each other in the confines of the monolith. Communication generally stays "inside the box" and within a single process. When you separate these components into services with their own processes, communication occurs externally over the network. These external calls are often slower and more susceptible to failure than the internal communication of the monolith. To combat this and other failure related challenges, anticipating and designing for failure has become an essential practice that accompanies the microservice approach. Design patterns that help minimize the risk of failure such as the circuit breaker pattern are common among microservices.
Consider a business that wants to leverage cloud infrastructure to host certain components of an application and internal infrastructure to host other more sensitive components. It's not very feasible to fulfill these requirements with a single monolithic application. Separating your system into multiple services is really the only option here. Although more complex, microservices provide deployment versatility that single process monoliths can't offer. A system of microservices can be deployed on various platforms, in various locations, and with various tools all at the same time. Monoliths, on the other hand, demand a meticulously configured environment and a strict deployment process. Since the monolith is essentially one thing it can really only be deployed in one place.
In 1968 Melvin Conway published a paper that discussed the sociological relationship between an organization and what it produces. His observations are often referred to as "Conway's Law".
Any organization that designs a system will produce a design whose structure is a copy of the organization's communication structure.
- Melvin ConwayEarly Computer Science Activist
Following this logic, if you wish to implement a microservices architecture, you'll need to reorganize your teams to reflect the microservices structure. Converting to microservice teams instead of layered teams based on technology (DBAs, BSAs, Service Team etc) can offer many benefits on its own.
Organized Around Function
Monolithic teams tend to be organized around horizontal technical layers (UI teams, business logic teams, database teams etc.) while microservice teams form around a specific functionality. Horizontally aligned teams tend to focus on their particular layer and can become removed from the core purpose of any specific system function.
For example, a database admin creating tables to store order details isn't necessarily concerned with or aware of the big picture when it comes to orders. They probably don't know how orders are created, how they're displayed, how they're manipulated, how the data is used, or general issues surrounding order functionality. They might not even be aware of why orders exist and what their main purpose is. If that database admin was instead a member of the order team they would be able to work closer with the individuals that use order data and may be able to make better decisions on how to store the data.
Less coordination is required between microservice teams when compared with monolithic teams. For example, monolith component teams must synchronize their deployment schedules since the monolith is released as one unit. Microservice teams have more freedom to deploy on their own schedules. No matter what approach you use, inter-team communication will always be necessary. But by internalizing team dependencies, microservice teams can minimize the time and effort involved in interfacing with external teams who may or may not be receptive to their needs.
Microservice teams are held completely responsible for their specific functionality. A single team determines what the service should do, how the service should be designed, what the business logic consists of, how to build their services, and how to run their service. Team members are more personally invested in the operational goals of their service. Monolithic teams, on the other hand, distribute responsibility across horizontal layer teams. These teams often struggle with who to hold accountable for a service not working correctly. Focusing accountability to a single team leads to less pointing fingers between data, services, and ui layers. Amazon pioneered the concept of operational responsibility on microservice teams and has seen much success come out of it.
Giving developers operational responsibilities has greatly enhanced the quality of the services, both from a customer and a technology point of view ... You build it, you run it. This brings developers into contact with the day-to-day operation of their software.
- Werner VogelsCTO Amazon.com
Focusing on a small bit of functionality allows service teams to shrink in size. Smaller teams are easier to manage and offer better team dynamics. Amazon CEO Jeff Bezos once decreed that working teams should be no larger than what two pizzas can feed. He strongly feels that small teams can communicate more effectively than large teams. Team members can get to know each faster and on a more personal level. It's easier to find points of contact on a smaller team. On-boarding new members also becomes easier since there are less people to become acquainted with and a smaller more focused scope to become familiar with.
[I]n these larger teams, people were lost. They didn't know who to call for help because they didn't know the other members well enough.
- Jennifer MuellerPsychologist and Management Professor
As monolithic systems grow and evolve over time they become bloated, overly complex, and inflexible. Making changes becomes challenging, time consuming, and risky. The core system structure is often incompatible with new technologies or tools. Support teams become large, inefficient, and hard to manage. These systems can grow to a point at which their own size and complexity inhibit their ability to evolve. Once a system can no longer evolve it becomes outdated and requires total overhaul.
By separating a monolith into individual microservices you can create a system that instead embraces evolution. Making changes to a microservice is relatively simple and low risk since the service is independent from other services. New languages, data storage solutions, or tools can be incrementally incorporated into certain components without affecting other components. Services can be deployed to different environments since they contain their own dependencies and communicate through standard protocols. Teams develop personal accountability for their service function and remain at a small, efficient, and manageable size.
Unfortunately, there is no one-size-fits all solution when it comes to software architecture. Microservices architecture may offer flexibility and adaptability but it comes at a cost of increased overhead and implementation challenges. A system must be analyzed to see if it's a good candidate for a microservices approach. Complex applications with many components that need to adapt and evolve with the times might benefit from a microservices architecture. Smaller lighter-weight applications that require only minor adjustments over time might be better off sticking with a traditional monolithic approach.
- Martin Fowler: Microservices
- Martin Fowler: A Microservices Premium
- Martin Fowler: Microservice Prerequisites
- Chris Richardson: Introduction to Microservices
- Neal Ford: Building Microservices Architecture 2015 NFJS Conference
- James Lewis: James Lewis on Microservices
- Werner Vogels: Learning from the Amazon Technology Platform
- The Science Behind Why Jeff Bezos's Two-Pizza Team Rule Works