logoTan Chia Chun

Monolith and Microservices

An introduction to monolithic architecture, microservices architecture, Docker, Kubernetes, and the trade-offs involved in designing scalable applications.

Introduction

Applications can be structured in different ways depending on product size, team size, deployment needs, and scaling requirements.

Two common architectural styles are:

  • Monolithic architecture
  • Microservices architecture

A monolith keeps most application features inside one codebase and usually deploys them as one unit.

A microservices architecture splits the application into smaller services, where each service owns a specific business capability and can be developed, deployed, and scaled independently.

Neither approach is always better.

The right choice depends on the trade-offs a team is willing to accept.


Monolith

A monolith is an application where most features are built, tested, and deployed together.

Common parts of the system may include:

  • User authentication
  • Business logic
  • API routes
  • Background jobs
  • Database access
  • Admin features

These parts usually live in the same codebase and are released as a single application.

Example

Client
  |
Monolithic Application
  |
Database

In this setup, the application may contain modules such as users, orders, payments, notifications, and reporting, but they are still part of one deployable system.

Advantages

Simpler Development

A monolith is usually easier to build at the beginning because everything lives in one place.

Developers can understand the full flow without jumping across multiple repositories, services, or deployment pipelines.

Easier Testing

End-to-end behavior is often easier to test because the application runs as a single unit.

There are fewer network boundaries between features.

Simple Deployment

A monolith can often be deployed with one build and one release process.

This makes deployment easier for small teams or early-stage products.

Easier Debugging

Logs, errors, and stack traces are usually easier to follow because most of the logic runs in one application.

There is less distributed tracing required compared with microservices.

Lower Operational Complexity

A monolith does not require service discovery, distributed monitoring, network retries, or complex orchestration at the start.

This reduces infrastructure overhead.

Disadvantages

Harder to Scale Specific Features

Because the application is deployed as one unit, scaling one feature often means scaling the entire application.

Example:

High traffic in payments -> Scale the whole monolith

This can waste resources if only one module is under heavy load.

Slower Deployments Over Time

As the codebase grows, builds and test suites may become slower.

Even small changes can require redeploying the whole application.

Tighter Coupling

Modules inside a monolith can become tightly coupled if boundaries are not maintained.

This makes changes riskier because one part of the system may unexpectedly affect another.

Technology Lock-In

A monolith usually shares one main technology stack.

It can be difficult to introduce a different programming language, database, or framework for one specific feature.

Larger Blast Radius

A bug in one module can potentially affect the entire application if isolation is weak.

For example, a memory leak in a reporting module might slow down authentication or checkout.


Microservices

Microservices architecture breaks an application into multiple small services.

Each service focuses on a specific business capability.

Common examples include:

  • User service
  • Order service
  • Payment service
  • Notification service
  • Inventory service

Each service can have its own codebase, database, deployment pipeline, and scaling rules.

Example

Client
  |
API Gateway
  |
  +-- User Service ------ User Database
  +-- Order Service ----- Order Database
  +-- Payment Service --- Payment Database
  +-- Notification Service

Services communicate through APIs, message queues, or event streams.

Advantages

Independent Deployment

Each service can be deployed separately.

A team can update the payment service without redeploying the user service or order service.

Independent Scaling

Services can scale based on their own traffic patterns.

Example:

Payment Service -> 3 instances
Notification Service -> 10 instances
Reporting Service -> 1 instance

This can improve resource usage at scale.

Better Team Ownership

Different teams can own different services.

This works well in larger organizations where teams need autonomy over specific domains.

Technology Flexibility

Each service can choose the technology that best fits its problem.

For example:

  • Node.js for an API service
  • Go for a high-performance service
  • Python for machine learning jobs
  • PostgreSQL for relational data
  • Redis for fast temporary data

Fault Isolation

When designed well, a failure in one service does not need to bring down the whole system.

For example, if the notification service is unavailable, users may still be able to place orders.

Disadvantages

Higher Operational Complexity

Microservices introduce more moving parts.

Teams must manage:

  • Service discovery
  • Load balancing
  • Distributed logging
  • Monitoring
  • Tracing
  • Deployment pipelines
  • Network failures

The system becomes harder to operate.

Distributed System Challenges

Once services communicate over the network, new failure modes appear.

Examples include:

  • Timeouts
  • Retries
  • Partial failures
  • Duplicate messages
  • Eventual consistency
  • Version mismatch between services

These problems are usually harder than normal in-process function calls.

Data Consistency Becomes Harder

In a monolith, a transaction may update multiple tables in one database.

In microservices, each service may own its own database, so cross-service changes are harder to coordinate.

Example:

Create Order
  |
  +-- Reserve Inventory
  +-- Charge Payment
  +-- Send Confirmation

If payment succeeds but inventory reservation fails, the system needs a recovery strategy.

More Expensive Infrastructure

Running many services usually requires more infrastructure than running one application.

Costs may increase because of:

  • More containers
  • More databases
  • More monitoring tools
  • More deployment environments
  • More network traffic

Harder Debugging

A single user request may travel through many services.

To debug production issues, teams often need distributed tracing and centralized logs.

Request -> API Gateway -> Order Service -> Payment Service -> Notification Service

Without good observability, finding the root cause can be difficult.


Docker

Docker is a platform for packaging applications into containers.

A container includes the application code and the runtime environment needed to run it.

This helps applications run consistently across different machines.

Why Docker Matters

Without containers, developers often face environment differences such as:

  • Different Node.js versions
  • Missing system dependencies
  • Different operating system behavior
  • Configuration differences between local and production

Docker reduces these issues by packaging the application environment.

Example

Application Code
  +
Runtime Dependencies
  +
System Libraries
  =
Docker Image

A Docker image can be started as a container.

Docker Image -> Running Container

Docker in Monoliths

For monoliths, Docker makes deployment more consistent.

The whole application can be packaged into one image and deployed to a server or cloud platform.

Monolith -> Docker Image -> Container

Docker in Microservices

For microservices, each service is commonly packaged as its own Docker image.

User Service -> user-service image
Order Service -> order-service image
Payment Service -> payment-service image

This makes it easier to deploy, scale, and version services independently.


Kubernetes

Kubernetes is a container orchestration platform.

It helps run, scale, restart, and manage containers across a cluster of machines.

Docker packages applications into containers.

Kubernetes manages those containers in production.

Why Kubernetes Matters

In production, teams need more than just starting containers manually.

They need to handle:

  • Service discovery
  • Load balancing
  • Auto scaling
  • Rolling deployments
  • Health checks
  • Container restarts
  • Configuration management
  • Secret management

Kubernetes provides patterns and tools for managing these concerns.

Example

Kubernetes Cluster
  |
  +-- Node 1
  |    +-- User Service Container
  |    +-- Order Service Container
  |
  +-- Node 2
       +-- Payment Service Container
       +-- Notification Service Container

If a container crashes, Kubernetes can restart it.

If traffic increases, Kubernetes can run more instances.

If a new version is released, Kubernetes can gradually roll it out.

Kubernetes with Monoliths

A monolith can run on Kubernetes as one application container.

This can still provide:

  • Health checks
  • Rolling deployments
  • Auto restarts
  • Horizontal scaling

However, the monolith still scales as one unit.

Kubernetes with Microservices

Kubernetes is commonly used with microservices because it can manage many services across many containers.

It helps coordinate:

  • Multiple service instances
  • Internal networking
  • Service-to-service communication
  • Independent scaling
  • Rolling updates per service

Kubernetes is powerful, but it also adds operational complexity.

It is usually more useful when the system is large enough to justify that complexity.


Monolith vs Microservices

AreaMonolithMicroservices
CodebaseOne main codebaseMultiple service codebases
DeploymentDeployed as one unitServices deployed independently
ScalingScale the whole applicationScale individual services
DebuggingEasier to trace locallyRequires distributed tracing
DataOften one shared databaseOften database per service
ComplexityLower at the startHigher operational complexity
Best ForSmall teams, early products, simpler domainsLarge teams, complex domains, high-scale systems

When to Choose:

Monolith

A monolith is often a good choice when:

  • The product is still early
  • The team is small
  • The domain is not clearly separated yet
  • Fast iteration is more important than independent scaling
  • Operational simplicity matters

Many successful systems start as monoliths.

A well-structured monolith with clear modules can scale surprisingly far.

Microservices

Microservices are usually a better fit when:

  • The system has clear domain boundaries
  • Different teams own different business areas
  • Some services need to scale independently
  • Independent deployment is important
  • The team has strong DevOps and observability practices

Microservices should solve real organizational or technical problems.

They should not be introduced only because they sound modern.


Conclusion

Monoliths and microservices are both valid architectural styles.

A monolith favors simplicity, fast development, and easier operations at the beginning.

Microservices favor independent deployment, independent scaling, and team autonomy, but they introduce distributed system complexity.

Docker helps package applications consistently, whether the application is a monolith or a set of microservices.

Kubernetes helps manage containers in production, especially when systems grow large and require orchestration.

For many teams, the best path is to start with a clean, modular monolith and move toward microservices only when the benefits clearly outweigh the complexity.


References

Microservice Architecture

Amazon - Monolithic vs. Microservices Architecture

Martin Fowler - Microservices

On this page