Monolith vs Microservices
Monolith vs Microservices
Every software system starts somewhere. Most start as a monolith — a single deployable unit where every feature lives in the same process. Microservices are a deliberate reaction to the problems that monoliths develop at scale. This lesson examines both architectures honestly, because the single most important skill you need before adopting microservices is knowing when not to.
What a Monolith Actually Is
A monolith is not a synonym for "bad code." It is a deployment topology: one build artifact (.jar, .war, a container) that packages the entire application. Spring Boot applications are monoliths by default. When you run mvn spring-boot:run, your controllers, services, repositories, and domain logic all start in a single JVM.
Monoliths come in flavours. A modular monolith enforces clear package boundaries and dependency rules inside a single deployable. A big-ball-of-mud monolith has no enforced structure. The second kind is what people usually mean when they complain about monoliths — but the architecture itself is not the cause of that mess; lack of discipline is.
The Problems That Make Teams Reach for Microservices
Monoliths develop predictable pain points as a codebase grows and teams expand. Understanding these pain points tells you whether microservices are actually the right cure.
- Deployment coupling. A bug in the payments module forces you to redeploy the entire application — including the unrelated user-profile module. Risk and blast radius grow with the size of each release.
- Scaling inflexibility. If only your image-processing feature is under load, you still have to scale the whole application. A single process means a single scaling unit.
- Technology lock-in. Every part of the system must use the same language, runtime, and dependency versions. A team wanting to experiment with a different database or framework cannot do so without affecting everyone.
- Team autonomy bottleneck. When twenty developers work in a single codebase, merge conflicts, test suite ownership, and shared release coordination become significant overhead.
- Availability coupling. A memory leak or uncaught exception in one part of the process can crash the entire application.
What Microservices Trade for Those Solutions
Microservices decompose the application into independently deployable services, each owning its own data and communicating over the network. The Spring ecosystem gives you the building blocks: Spring Boot for each service, Spring Cloud for cross-cutting concerns, and a service registry such as Eureka or Consul for discovery.
A minimal two-service skeleton illustrates the shape. The order-service calls the inventory-service over HTTP:
What looks clean in a diagram carries real distributed-systems costs:
- Network latency and partial failure. The HTTP call to
inventory-servicecan time out, return a 503, or never arrive. In a monolith, calling a service is a method call — effectively free and always synchronous. - Distributed tracing. A single user request now spans multiple processes. Without a correlation ID propagated in headers and a tool like Zipkin or OpenTelemetry, debugging is guesswork.
- Data consistency. You cannot wrap the inventory check and the order insert in a single database transaction. You need eventual consistency patterns (sagas, outbox) that simply do not exist in a monolith.
- Operational surface area. Ten services means ten CI/CD pipelines, ten container images, ten health endpoints to monitor, ten sets of secrets to rotate, and ten places for configuration drift.
The Security Dimension
Security changes fundamentally when you decompose a monolith. In a monolith, Spring Security enforces authentication once at the edge, and every subsequent method call is implicitly trusted. In a microservices system, every service-to-service call crosses a network boundary and is a potential attack surface.
The standard answer is JWT propagation: the API gateway validates the incoming token, then forwards it (or a service-account token) to downstream services. Each service independently verifies the token signature.
You now have to manage key rotation, token expiry, and service-to-service trust across every service — complexity that is entirely absent in a monolith where one SecurityFilterChain covers everything.
When Microservices Are Worth It — and When They Are Not
Use this as a practical checklist before committing to the split:
- Team size and autonomy matter more than code size. If two teams need to deploy the same module on different schedules without coordinating, that is a genuine microservices use case. If one team owns everything, a modular monolith gives most of the benefit at a fraction of the cost.
- Start with a monolith, extract later. You almost never know the right service boundaries upfront. Prematurely splitting creates wrong boundaries that are expensive to undo once the services have separate databases and separate teams.
- Independent scaling requirements are a strong signal. If one part of your system needs 10× the compute of another, and those parts can be cleanly separated, that is a real reason to split.
- Organisational maturity matters. Microservices require DevOps capability: automated deployment, container orchestration, centralised logging, distributed tracing, and service mesh or mTLS. A team without those practices will drown in operational overhead.
Practical Starting Point
If you are building a new system, start here: structure your Spring Boot application as a modular monolith with clear package boundaries (com.example.orders, com.example.inventory). Define explicit interfaces between modules. Avoid cross-module direct field access. When a module's deployment or scaling requirements genuinely diverge — and you can observe that divergence in production data — extract it into a separate service at that point.
This approach gives you a clean extraction path because you have already thought about boundaries, without paying the operational tax of microservices before you need to.
Summary
Monoliths and microservices are deployment topologies, not quality levels. Monoliths are simpler to build, test, debug, and secure. Microservices solve real problems — independent deployment, fine-grained scaling, and team autonomy — but introduce distributed-systems complexity, network failure modes, and significant operational overhead. The right architecture is the one that fits your team size, deployment cadence, and operational maturity. For most teams at most stages, that means starting with a well-structured monolith and migrating selectively, not rewriting everything as services from day one.