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?
Benefits of Microservices
In addition to the points noted above, there are other practical benefits of adopting a microservice architecture.
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.
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.