Monolith vs Microservices
Before reaching for Spring Cloud, you need to know what problem microservices actually solve. Monolith and microservices are two ends of an architecture spectrum, and the right choice depends on your team, your scale, and your tolerance for operational complexity. This page defines both, compares them honestly, and introduces the modular monolith as the pragmatic middle ground most teams should start from.
What is a monolith?
A monolithic application packages all functionality — web layer, business logic, data access — into a single deployable unit. In Spring Boot that is one executable JAR built from one codebase, running in one process, talking to (usually) one database.
┌─────────────────────────────────────────┐
│ orders-app.jar │
│ ┌─────────┐ ┌─────────┐ ┌────────────┐ │
│ │ Orders │ │Inventory│ │ Payments │ │
│ │ module │ │ module │ │ module │ │
│ └─────────┘ └─────────┘ └────────────┘ │
│ in-process method calls │
└──────────────────┬───────────────────────┘
│ JDBC
┌──────▼──────┐
│ one schema │
└─────────────┘
Calls between modules are ordinary Java method calls — fast, transactional, and type-safe. A single @Transactional boundary can span orders, inventory, and payments atomically.
What are microservices?
A microservices architecture splits the application into independently deployable services, each owning a bounded slice of the business and (critically) its own data. Services communicate over the network — HTTP/REST, gRPC, or messaging — never by reaching into each other’s database.
┌──────────┐ ┌──────────┐ ┌───────────┐
│ Orders │ │Inventory │ │ Payments │
│ service │ │ service │ │ service │
└────┬─────┘ └────┬─────┘ └─────┬─────┘
│ HTTP / events │ │
┌──▼──┐ ┌──▼──┐ ┌──▼──┐
│ DB │ │ DB │ │ DB │
└─────┘ └─────┘ └─────┘
Each service can be written, deployed, scaled, and even rewritten in isolation. The trade-off: a method call becomes a network call, and a database transaction becomes a distributed coordination problem.
Side-by-side comparison
| Dimension | Monolith | Microservices |
|---|---|---|
| Deployment | One artifact, one deploy | Many artifacts, independent pipelines |
| Scaling | Scale the whole app (vertical or replicas) | Scale hot services independently |
| Data | Shared schema, ACID transactions | Database-per-service, eventual consistency |
| Team | One codebase, coordination overhead grows | Teams own services end-to-end (autonomy) |
| Tech diversity | One stack | Each service can pick its stack |
| Complexity | Code complexity in one place | Operational + distributed-systems complexity |
| Failure isolation | One bug can take down everything | A failing service degrades only its area |
| Latency | In-process calls (nanoseconds) | Network hops (milliseconds) + retries |
| Testing | Easy end-to-end in one process | Needs contract tests, integration envs |
| Local dev | Run one app | Run/mocks for many services |
Note: Microservices trade code complexity for operational complexity. You don’t remove complexity — you move it into the network, deployment, and observability.
Scaling, in practice
A monolith scales by running more identical copies behind a load balancer. That works well until one part of the app — say a CPU-heavy PDF renderer — dominates resource use, forcing you to over-provision the entire app to scale one feature.
Microservices let you scale only what’s hot:
Orders service: 12 replicas (high traffic)
Inventory service: 3 replicas (moderate)
Reporting service: 1 replica (nightly batch)
This granularity is a genuine win — but only if your traffic profile is actually uneven enough to justify the overhead.
Data: the hard part
In a monolith a single transaction keeps orders, inventory, and payments consistent:
@Transactional
public Order placeOrder(OrderRequest req) {
inventory.reserve(req.items()); // same DB, same tx
Order order = orders.create(req);
payments.charge(order); // all-or-nothing
return order;
}
Split those into three services with three databases and you can no longer wrap them in @Transactional. You must coordinate with the Saga Pattern and accept eventual consistency. This is the single biggest cost of microservices, and the most underestimated.
The modular monolith: the pragmatic middle ground
Most teams do not need microservices on day one. A modular monolith keeps the single-deployment simplicity while enforcing clean module boundaries inside the codebase — so that if you later need to extract a service, the seams already exist.
Principles:
- Package by feature, not by layer.
com.shop.orders,com.shop.inventory,com.shop.payments— each with its own controllers, services, and repositories. - No reaching across modules. Modules talk through published interfaces or Spring
ApplicationEvents, not by autowiring each other’s internal beans. - Own your tables. Each module owns its tables; no foreign keys crossing module boundaries.
// orders module publishes an event instead of calling inventory directly
@Service
@RequiredArgsConstructor
public class OrderService {
private final ApplicationEventPublisher events;
@Transactional
public Order placeOrder(OrderRequest req) {
Order order = repository.save(Order.from(req));
events.publishEvent(new OrderPlaced(order.getId(), req.items()));
return order;
}
}
// inventory module listens — a clean seam to later become a network boundary
@Component
class InventoryListener {
@EventListener
void on(OrderPlaced event) { /* reserve stock */ }
}
Spring Modulith (org.springframework.modulith) can verify these boundaries at build time and even publish events across a transactional outbox — making the eventual extraction to microservices low-risk.
Tip: Start with a well-modularized monolith. Extract a service only when a specific module has a concrete need: independent scaling, a separate team, or a different release cadence. “We might need it” is not a reason.
Choosing
| If you have… | Prefer |
|---|---|
| A new product, small team, unproven domain | Modular monolith |
| Clear bounded contexts + multiple teams | Microservices for the high-friction parts |
| Wildly uneven scaling needs | Extract the hot path to a service |
| A small team and a stable product | Stay a monolith |
The next page, Why Microservices?, digs into the real drivers and costs so you can make this call deliberately.