Monolith vs Microservices
BeginnerMonoliths are single deployable units; microservices split the system into independently deployable services, each owning its data and a well-defined API boundary.
Overview
A monolith is a single, unified deployment unit where all modules share the same process, database, and release cycle. It is simple to develop and test at small scale but becomes a bottleneck as teams grow: every change requires a full redeployment, scaling the whole app for a single hot component is wasteful, and large codebases become hard to reason about. Microservices decompose the system into small, independently deployable services each owning its own data. The trade-off is significant operational complexity: distributed systems require service discovery, distributed tracing, eventual consistency, and sophisticated deployment pipelines.
Monolith Architecture
A monolith packages all modules (UI, business logic, data access) into a single deployable unit. It shares a single database, a single codebase, and one CI/CD pipeline. Great for startups and small teams; painful at scale.
// Monolith — all modules in one Spring Boot application
@SpringBootApplication
public class ShopApp { ... }
// Modules co-located in the same JVM:
// com.shop.orders → OrderService, OrderController
// com.shop.inventory → InventoryService, InventoryController
// com.shop.billing → BillingService, InvoiceController
// Single database — all modules query the same schema
@Service
public class OrderService {
// Direct method call to InventoryService — in-process, no network
public Order placeOrder(PlaceOrderRequest req) {
inventoryService.reserve(req.getItems()); // same JVM
billingService.charge(req.getCustomerId(), total);
return orderRepository.save(order);
}
}
// Pros:
// + Simple local development (one service to run)
// + ACID transactions across all modules
// + Easy debugging (single log stream, one JVM)
// Cons at scale:
// - Any change requires deploying the whole app
// - Team coordination friction — merge conflicts, slow releases
// - Can't scale individual components independentlyMicroservices Architecture
Microservices decompose the system by business capability. Each service is a separate deployable unit with its own database. Services communicate over HTTP/REST or async messaging. Teams deploy independently.
// Microservices — separate deployments
// order-service → owns orders DB
// inventory-service → owns inventory DB
// billing-service → owns invoices DB
// Cross-service call via HTTP (synchronous)
@Service
public class OrderService {
private final InventoryClient inventoryClient; // Feign/WebClient
private final BillingClient billingClient;
public Order placeOrder(PlaceOrderRequest req) {
// Network call to inventory-service
inventoryClient.reserve(req.getItems());
// Network call to billing-service
billingClient.charge(req.getCustomerId(), total);
return orderRepository.save(order);
// Problem: no distributed ACID — need Saga pattern for consistency
}
}
// Or async via Kafka (event-driven)
public Order placeOrder(PlaceOrderRequest req) {
Order order = orderRepository.save(order);
eventPublisher.publish(new OrderPlaced(order.getId(), req.getItems()));
// inventory-service and billing-service react asynchronously
return order;
}Choosing the Right Architecture
Microservices are not always better. Martin Fowler's "Microservices Premium" — you pay a complexity tax upfront. Start with a modular monolith and extract services only when scaling pain or team autonomy demands it.
// Decision framework
// Favour MONOLITH when:
// ✓ Team < 10 engineers
// ✓ Domain is not yet well understood (bounded contexts unclear)
// ✓ Early-stage product — need to iterate quickly
// ✓ Simple infrastructure (no Kubernetes, service mesh overhead)
// Favour MICROSERVICES when:
// ✓ Multiple autonomous teams (each owns one or more services)
// ✓ Services have different scaling profiles (payment CPU-bound, search IO-bound)
// ✓ Different tech stacks needed per component (ML service in Python, API in Java)
// ✓ High availability requirements — independent deployments reduce blast radius
// Modular Monolith — the pragmatic middle ground
// One deployable unit BUT strong module boundaries enforced by code
// Module A cannot directly access Module B's database layer
// Easy to extract to microservice later when the boundary is proven
// Recommended migration path: Modular Monolith → Strangler Fig → MicroservicesKey Points to Remember
- 1Monolith: single deployment, shared DB, simple dev — becomes a bottleneck at team/scale growth.
- 2Microservices: independently deployable, each owns data — high operational complexity.
- 3Network calls replace method calls — introduce latency, partial failure, and eventual consistency.
- 4Microservices require service discovery, distributed tracing, circuit breakers, and saga patterns.
- 5Start with a modular monolith and extract services when pain demands it (Strangler Fig).
- 6Team topology matters: Conway's Law says software structure mirrors communication structure.
Interview Questions
Sign in to ask AriaWhat are the main trade-offs between monolith and microservices architectures?
When would you NOT migrate from a monolith to microservices?
How do you maintain data consistency across microservices without distributed transactions?
What is a modular monolith and how does it differ from a traditional monolith?
Explain Conway's Law and how it influences microservices design.
Ask Aria about Monolith vs Microservices
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.