The past ten years marked a significant change in how software teams build and deploy applications. We moved away from bulky, slow, monolithic applications toward lightweight, scalable, distributed service-based applications. Meanwhile, tools like Docker, Kubernetes, and other container platforms helped accelerate this process. Despite this sudden growth, a fundamental question remains: what exactly is a service, and how does it fit into a microservice architecture?
In this blog, we answer this question by explaining the history of microservices, the concept of services as defined by microservices, and how teams deploy services today.
In the early days of enterprise software development, applications were often developed and deployed as a single executable package called a monolith. The monolith contained everything needed to run the application, including business logic, user interface, and request routing. Some things were handled externally, like databases for data persistence and firewalls for security, but developers bundled most things directly related to the application into a single package.
While this approach is relatively easy to manage, it has a lot of drawbacks. For one, running the monolith requires a lot of computing resources since you must run every part of the application simultaneously. This makes it hard to update, redeploy, or scale monoliths in a reasonable amount of time since it takes a while to deploy and start up new instances. Size constraints also limit the number of instances you can run on a single host, assuming the application even supports multiple instances simultaneously. On top of all of this, if the monolith crashes or fails, restarting it can take minutes, if not longer.
One alternative to monoliths is a service-oriented architecture or SOA. In an SOA, applications are split into discrete units of functionality that provide a specific set of functionality. These services provide interfaces for communicating with other services over a network, and their combined functionality is what creates the complete application. This has drawbacks such as a more complex architecture, more moving parts, and higher latency due to network traffic. Still, the benefit is that each service can be restarted, redeployed, updated, and even scaled without affecting other services.
SOA has been around since the late 1990s and most recently has taken the form of microservices.
What makes a service a service? While there are dozens of definitions, they commonly share these characteristics:
- Services are lightweight. Since services have a fraction of the capabilities of a complete monolithic application, they're a fraction of the size. This makes it easier to run multiple services on a single host, migrate a service between hosts, and restart a service quickly—often just a few seconds compared to several minutes for a monolith.
- Services have discrete and clearly defined boundaries. Each service performs a specific function that no other service provides. It exposes this functionality to other services via API, RPC, or similar so that other services can consume it. Services don't need to be aware of what other services are doing, only how to interact with them.
- Services are independently manageable. Services shouldn't depend on other services to function. Our service can interact with other services, but we should build it with the assumption that other services might not always be available or might be slow. We should build resilience mechanisms like timeouts and retries in case a critical dependency is unavailable or unresponsive so it doesn't significantly impact our service.
- Services are easily scalable. Services should be easy to scale and replicate. We should be able to increase the total compute capacity available to a service by deploying additional instances of it, and we should be able to add or remove instances without impacting throughput.
With these qualities in mind, let's look at an example using a banking application. Imagine a retail bank called the Bank of Anthos. The bank currently runs on a monolith but wants to migrate to a microservice architecture. As they considered how to break out their monolith into distinct functions, they created the following diagram:
Each box represents a unique service. For example, two databases—ledgerdb and accountsdb—store persistent data about user accounts and transactions. Five other services use this data to perform functions like authenticating users, reading and writing transactions, and checking account balances. The frontend service then takes all this data and presents it to the user in a web interface.
Note that each service communicates with at least one other service. Because each service has clearly defined interfaces, other services can interact with it. For example, the balancereader serves data to both the frontend and ledgerwriter, but this also means that the frontend and ledgerwriter need to be designed in a way that they won't fail if the balancereader is unavailable.
Migrating from monoliths to microservices also changes the development process. In a monolith, multiple teams contribute to a single codebase. Combining these changes creates a lot of administrative overhead, requiring techniques like code freezes and merge windows. Still, releasing even a small change to production could take hours, days, or even weeks.
With microservices, teams gain much more autonomy over services because each service operates independently. If one team needs to deploy a new feature or hotfix, they can do so without coordinating with other teams or rushing to make a release window. The most common way teams deploy and manage services today is with container orchestration platforms like Kubernetes. Containers let developers bundle their services into portable packages that they can easily deploy to one or more hosts, and container orchestrators like Kubernetes automate deploying, managing, scaling, and monitoring containers across multiple hosts.
The goal of microservices is an architecture where teams can deploy and scale applications faster, fix defects faster, and optimize resource consumption more efficiently than with a monolithic architecture.
Service-based architectures—microservices in particular—have radically changed how many teams build, deploy, and manage software. Now that you know how they work, consider the other questions that might arise when developing a service, such as: