Microservices vs Monolithic
The term microservice suggests a service that is very small. But, how small? Does a microservice become a monolith after a certain size? Is a service something that is in between? The answer: there is indeed a spectrum between nano-services and monoliths. Once we reach monolith in this spectrum, there is only one (if the context is software architecture). However, within the software industry, it is not uncommon to refer to a very large service in a distributed network of services as a monolith. Conversely, microservice can be a misnomer that simply refers to a service in a distributed system.
Below is a comparison of the trade-offs between monoliths and microservices. Only the positives (pros) are listed for both architectures — a positive for one architecture is a negative for the other.
|Resource Usage||All components of the system share the same pool of resources since they're in the same machine.||Components of the system are separated in different machines, virtual machines, containers, or a combination of all of these.|
|Scalability||-||Scaling a distributed system can be done surgically by scaling up just the components that require scaling, rather than scaling up the entire system.|
|Infrastructure Complexity||The infrastructure of a monolith is much simpler than a distributed system of microservices:||-|
|Development Complexity||Deploying a monolith is simpler as there's only a single application to build, test, and deploy. However, deploying changes can be riskier as a bug in any part of the application can potentially bring down the entire system. It's also harder to deploy changes frequently and independently.||-|
|Development Flexibility||-||Different components can use different technology stacks, which is beneficial for different use cases.|
|Data Management||Either monoliths or microservices can have a single or multiple databases. Depending on the architecture of the database schemas of multiple databases, there may be a need to keep data consistent between them via a transactional events-based system, like the Saga Pattern.|
A common way to deploy a microservice is in a container. Languages that compile and don’t require a VM (like Java’s JVM) to run require the least memory to work. For example, a minimal Golang program running in a container could run with only 10MB. A microservice running a server with only a few API endpoints and minimal routing logic could work with less than 50MB. This size of service would be referred to as a nanoservice (on the spectrum referenced in the Overview).
When a service is this small, it creates a lot of redundant overhead of the following for each container:
- The operating system’s userspace (non-Kernel set of libraries, utilities, and more) that needs to be duplicated per-container, which includes:
- Container runtime (such as Docker).
- System services (process management, logging, networking, and more).
- Operating system scheduler.
- Application runtime (rough estimate in MB: single-digit for compiled languages, double-digits for interpreted, and triple-digits for those requiring a VM like Java).
- Overhead of free space to prevent OOM (Out Of Memory crashes).
There is also redundant CPU overhead for all of the above.
No one-size-fits-all solution exists for the perfect size of a microservice. The perfect size is often discovered over several iterations of refactorings and versions of services over the lifetime of a system.
Sam Malayek works in Vancouver for Amazon Web Services, and uses this space to fill in a few gaps. Opinions are his own.