Modular monolith vs microservices — when to choose each. — Cracked Java
// High-Level Design (HLD / Distributed Systems) · Microservices, Service Mesh, API Gateway
SeniorSystem Design

Modular monolith vs microservices — when to choose each.

Modular monolith vs. microservices — when to choose each

The modular monolith is the architecture most teams should reach for before microservices, and naming it unprompted is one of the strongest senior signals in this topic. It captures the main internal benefit of microservices — clear, enforced boundaries between bounded contexts — without paying the distributed-systems tax.

What a modular monolith is

A single deployable unit (one process, typically one database) internally divided into modules with enforced boundaries: a module exposes a narrow public API and hides its internals, and the build fails if another module reaches across the boundary into private code. Modules communicate through explicit in-process contracts — direct calls or an in-process event bus — not by sharing each other's tables.

Spring Modulith is the concrete tooling in the Java/Spring world. It lets you declare module boundaries, verifies them at test/build time (so violations can't sneak in), and supports in-process domain events between modules — even publishing them through an outbox so the move to messaging later is incremental. The point: you get microservices-style modularity enforced by the compiler/tests rather than only by network boundaries.

The comparison

DimensionModular monolithMicroservices
DeploymentOne unit — deploy togetherIndependent per service
Inter-module callsIn-process — fast, reliableNetwork — slow, partial failure
TransactionsLocal ACID across modulesSaga / eventual consistency
Boundaries enforced byCompiler / build (Spring Modulith)Network + separate repos
ScalingWhole app scales togetherPer-service, independent
Operational overheadLow — one thing to runHigh — mesh, discovery, tracing
Refactoring boundariesCheap — it's all one codebaseExpensive — cross-repo, cross-team
Team autonomyLimited — shared deploy pipelineHigh — own the service end-to-end

When to choose each

Choose a modular monolith when:

  • The team is small-to-medium, or the product/domain boundaries are still moving. Refactoring a module boundary is a refactor; refactoring a service boundary is a migration.
  • You want cross-context business operations to stay within a single ACID transaction.
  • Operational simplicity matters more than independent scaling — one thing to deploy, monitor, and debug.
  • You suspect you will need microservices eventually: a well-modularized monolith with clean boundaries is the ideal starting point to extract services from later.

Choose microservices when:

  • Multiple teams need to deploy independently without coordinating releases (the deployment-coupling pain is real and recurring).
  • Specific components have genuinely different scaling profiles (one is CPU-bound and bursty, the rest steady).
  • Independent fault isolation or technology heterogeneity is a hard requirement.

Mark your status