Microservice architecture is a way of building systems where you separate components into small, separate projects that serve one purpose or functionality.
Microservice architecture is an approach to building systems that has emerged in the context of DevOps practices such as continuous integration, continuous delivery, and virtualization/containerization. It focuses on implementing multiple small, autonomous programs or "services" that communicate together to deliver a complex system or application.
Microservices borrow from an old software development philosophy: Programs should focus on doing one thing, and doing it well. This approach is a contrast to more traditional approaches, such as monolithic applications, where a single codebase contains multiple functionalities deployed as a unit. Monoliths become problematic over time as new features are added. The codebase grows and can become fragmented, poorly organized, and subject to bottlenecks.
In contrast to monoliths, microservices divide an entire application/service into separate, small programs that serve one purpose or functionality. The individual components are smaller, easier to maintain, and can be scaled independently. More sophisticated functionality is achieved by combining pieces together.
Frustrations of Monolithic Applications
Where does this architectural pattern come from and why is it desirable?
As developers extend the features of an application, it grows in complexity. Perhaps you are working on a web application to sell books, for example.
Initially, the site needs to be able to show a title, its price, and reviews. Over time, though, the owners of the business might decide that the same site should be used to sell music and videos. The additional functionality -- such as storing video files and transcoding to multiple formats and embedding media players for previews -- is likely to be implemented as extensions to the original codebase. Without care and thought as to how the application should grow over time, functionality is likely to mix between the different modules and libraries. As this application continues to evolve, becoming ever more complex, the effort to maintain grows and it will be necessary to split it into functional components.
Web applications are unique in that their codebases already have a natural segmentation based on where the code needs to run. Consider Figure 1, which shows three pieces (and is representative of a web application like that described above):
- Data-access Layer - a database which stores the application state
- Business Logic - server-side logic which interacts with the database, may expose an API to interact with data, or manages the server-side rendering of the client interface
Monolithic applications group the three components into one as a single, logical executable. This structure made sense when initially developing the application because it allowed common functionality to be grouped without redundancy and provided a straightforward way to manage the deployment. All functions of the application execute within a single process and the features are divided up using the different features of the programming language in which the application is written.
Issues with Monoliths
As the monolith grows, the application invariably becomes more complex. As new code is added, care and time is required to organize functionality so that there are no conflicting issues or unnecessary code duplication. Code sprawl makes it more difficult to fix bugs or implement new features. Over time, other issues arise:
- Change cycles are tied together - Since monoliths tightly group the three components of an application, updating the UI requires testing, debugging, and deployment of all components, including the database and server logic. Rebuilding and deploying an entire monolithic application is very time consuming and prevents frequent releases.
- Isolating components is difficult - Keeping different components modular requires forethought and planning. In most codebases, there is often a degree of overlap between modules, and that is when functionality starts to creep. This makes it more difficult to keep changes that only affect one module from impacting others.
- Components are tightly coupled - Since each component is strictly tied to the others, changes in separate functions can be problematic across the entire application lifecycle. If certain parts of an application require more resources, the entire application needs to be scaled rather than only the needed services.
- Systems maintenance is frustrating - If a failure in the application occurs, the whole system needs to be taken offline in order to fix it. This adds a lot of downtime to the service because as you fix issues it can require a significant amount of time to re-test and redeploy.
Microservices address the frustrations of monoliths by dividing the components of a codebase into sets of smaller processes that can communicate with services using a well-defined interface. A familiar analogy for microservices is the relatability with a bee's honeycomb:
"In the real world, bees build a honeycomb by aligning hexagonal wax cells. They start small, using different materials to build the cells. Construction is based on what is available at the time of building. Repetitive cells form a pattern and result in a strong fabric structure. Each cell in the honeycomb is independent but also integrated with other cells. By adding new cells, the honeycomb grows organically to a big, solid structure. The content inside each cell is abstracted and not visible outside. Damage to one cell does not damage other cells, and bees can reconstruct these cells without impacting the overall honeycomb." - O'Reilly
Each microservice is built around business goals for the overall application and is scoped to a single purpose. Each service is then executed in its own process, which allows it to be scaled independently from the rest of the application. If a particular service needs to be shut down, that single process can go down for maintenance without taking the entire system offline.
What makes up a Microservice?
Microservice-based components should largely focus on one major piece of functionality. Each service has its own codebase and runs in its own process. This makes it different than using libraries as components, as the separation of code and functionality is enforced by the runtime environment and provides isolation whereas a library in a monolith is part of the larger application and the whole system must be taken offline in order to redeploy.
A second major benefit provided by grouping components as services is that it becomes necessary to provide an explicit, published interface that other parts of the system then use. This strengthens API contracts and generally improves the quality of code.
Organized by Business Capability
In traditional development teams, technical groups are often split by discipline. This will often result in a database team, a UI team, a backend team, and an operations team, among others.
Organizations that strongly follow a microservices strategy will organize teams based on the business capabilities. Rather than a database team, there instead might be an e-commerce team, a content team, etc. The goal is to allow teams to own their own products from conception, through implementation, and onto deployment. This provides a sense of ownership and often results in better products.
Smart Endpoints and Dumb Pipes
Microservices attempt to contain their intelligence and functionality available from "endpoints." This in contrast to putting that intelligence or logic into a protocol. Consider the difference between a protocol such as HTTP which can be used to transmit nearly any type of data as compared to SOAP, which is highly optimized for remote procedure calls (RPC).
There are two common protocols often used for microservices (one synchronous and the other asynchronous): HTTP (often used to expose a type of REST interface) and lightweight messaging protocols such as the advanced messaging queuing protocol (AMQP) or the Apache Kafka protocol.
Right Tool for the Job
A benefit of splitting a monolith’s functionality is that it allows the use of different programming languages or libraries in the building of components. Communication happens over common protocols that are supported by nearly every programming language, which means that almost any toolset might be used to solve a problem.
Decentralized Data Management
In monolithic applications, data storage decisions are often based on the requirements of the most stringent components of the system without consideration of how that might affect other pieces. In a sales application, for example, batch analytics processes that could work using hourly, daily, or even weekly snapshots of the data might end up bottle-necked by systems requiring up-to-the-second accuracy (such as systems calculating availability or pricing).
In microservices, it’s possible to separate such concerns and allow each component to work with the data it needs. Batch analytics might draw from archived data saved in a data lake while availability is determined by reading from a streaming data backend such as Kafka. Utilizing the right data source for the application frees up the database to more efficiently handle queries from real-time systems without putting undue pressure on either piece.
Design for Failure
While microservices provide many benefits, they can also bring complexities that don't apply to monoliths. Since microservice-driven applications are distributed systems that communicate across a network, they have to deal with network interactions. Monoliths either succeed or fail as a unit, they are either available or not. Microservices are more granular, part of the system might be up while another is down. This can make it a headache to figure out what's wrong.
Because a service might fail without warning, outages need to be both detected and restored quickly. Likewise, the system needs to be able to function if an associated component is unavailable. As a result, teams that manage microservices often place a strong emphasis on real-time monitoring of applications where they watch both performance (such as the number of requests the service might process per second, or the amount of time required to execute a query) as well as relevant business metrics.
Benefits of Microservices
In addition to the points noted above, there are other practical benefits of adopting a microservice architecture.
Because they are process-based and communicate across the network, individual services can communicate with each other even when the processes are distributed on different machines.
This allows for new features and functionality in the context of DevOps (IaC, monitoring, logging, CI/CD, and FaaS) to be implemented into and for important cross-service functionality (for example security, authentication, or authorization) to itself become a service.
As these interactions are discovered, automated builds can be implemented so that each deployment of service is consistently configured properly and cross-service interactions are tested as part of continuous integration.
Practices that speed up the processes of the application lifecycle can be implemented. Continuous integration and continuous deployment (CI/CD) is a practice derived from DevOps that automates the downstream process of updating your services. From the moment a developer commits a changelog in a service, automation will build, test, stage, and deploy the code to the production environment.
It is often easier to stage and test microservices as compared to monoliths, and as an extension, build automated pipelines around them.
Features are easier to add to your application because each service can be independently tested and deployed. This prevents features conflicting with each other, which can cause delays and blocks in the development process.
Independence and Isolation
With independent services making up a distributed computing system, orchestration software can be used to manage services automatically. If a service is resource hungry it can be automatically moved to another machine without affecting other services. Similarly, vulnerabilities in one component can often be isolated without spilling over into other parts of the infrastructure.
A major problem with monoliths is that if a specific service needs to be scaled up, the entire application requires scaling. In a distributed microservice architecture, independent services can scale up based on their own needs.
This can often save money in cloud environments as you are only paying for the computing resources you need.
Monoliths either succeed or fail as a whole. With one service failing in a monolith, the entire program goes down.
Microservices act as a distributed system where one piece may fail without affecting the rest of the application.
Who Uses Microservices?
Because of its flexibility, microservice architecture has become a popular way of building systems. It's been adopted by many companies working in technology, healthcare, real estate, and other industries.
Amazon is one of the pioneers of exposing platform functionality in the form of microservices. Microservice architecture is at the heart of its AWS cloud services, and nearly every type of computational resource available under the Amazon umbrella is deployed as a microservice. These include microservices allowing for computing (in the form of its serverless platform Lambda), storage and databases, network management and load balancing, messaging, logging/monitoring, and many more.
Netflix is a service that provides streaming video on demand. Its platform is a technology marvel, which during peak traffic times consumes 15% of the world's total Internet bandwidth. When Netflix first began its transition to video streaming, it struggled to keep up with user demand. This culminated in 2008 in an outage when a physical database became corrupted due to an infrastructure issue.
After the outage, Netflix aggressively adopted distributed architectures to prevent a similar issue. It organized each major piece of the service as a separate component capable of running in isolation and able to withstand outages of other pieces. Netflix has a highly efficient CI/CD pipeline which rigorously tests each part of the infrastructure each time code is committed. Additionally, they actively experiment in the system in production (a practice sometimes called Chaos Engineering) to ensure that each component is redundant.
While microservices provide many tangible benefits, there are also some drawbacks to their adoption.
- Monitoring features required - As each service is often deployed within a network environment, monitoring and logging are essential to provide visibility into what is happening with the application. Moreover, because the instances of a service may be distributed across a cluster, log aggregation is essential to have a cohesive view of the systems operations.
- Distributed computing - Distributed computing platforms are sophisticated frameworks that bring in their own challenges. They are parallel in nature, which requires coordination and closer attention to resource management. Additionally, more tooling is required to handle orchestration and administration.
- Automation is a necessity - Deployment and release processes are required for each microservice. If deployment processes are not automated, it can become overwhelming to manage the life-cycle and deployment of the application.
Software Used to Implement Microservices
The open-source ecosystem provides a wealth of tools that can be used to implement microservice architectures. Some of the most important technologies include:
- Docker - container management tool that packages software and makes it easy to move between runtime environments
- Kubernetes - orchestration tool that makes it possible to run containers on top of large clusters
- OpenShift - enterprise Kubernetes platform that provides robust workflow and identity management tools
- CoreOS - distributed computing platform including Container Linux (a lightweight container-centric OS), Prometheus (popular monitoring solution), and etcd (a key/value store used for configuration management) used broadly in container infrastructure
The New Method of Developing Enterprise Applications
Microservices offer an alternative to the traditional way of developing monolithic applications. They divide the complex interactions of large applications into separate components that run in their own processes and communicate using standard protocols. This allows for many benefits such as improved elasticity, decentralized data management, a better match between technologies and implementation challenges, and fault-tolerance.
Even though they may seem daunting to implement, the benefits of microservices pay off for complex enterprise applications. Not only can they speed up the release of new features and allow for integration with a more robust process (such as continuous integration and continuous deployment), but the entire application will be easier to manage, build, test, and deploy.
No results found