What I’m working on

(Concisely describe my current challenge and the question(s) I’m trying to answer)

Our project needs a modular backend architecture, but it’s not clear whether we should start with a modular monolith or go straight to microservices:

  • What are the characteristics of a modular monolith?
  • What are the pros and cons of modular monoliths compared to microservices?
  • Is a modular monolith a good compromise for us: starting simple, but keeping the option to move to microservices?

What I Learned

(Summarize new knowledge, reflections, or insights gained.)

I found it easiest to understand modular monoliths by looking at them as the middle ground between classic monoliths and microservices, looking particularly at how each architecture handles boundaries, deployment, and communication.

Boundaries

  • Classic Monolith: No real separation, everything is shared.
  • Modular Monolith: Logical boundaries (bounded contexts, contracts, internal API).
  • Microservices: Physical boundaries (separate deployments and DBs).

Deployment

  • Classic Monolith: Deploy as one unit, scale all-or-nothing.
  • Modular Monolith: Still one deployable, but structured for later extraction to microservices.
  • Microservices: Independent deployments, each service scales separately.

Communication

  • Classic Monolith: One application, code calls code.
  • Modular Monolith: One application where modules interact via:
    • Direct method calls internally in modules (simple, fast)
    • In-process events to notify other modules (looser coupling, pub/sub)
    • Application messaging between modules (loosest coupling, mediator pattern)
  • Microservices: Separate applications communicate via HTTP/gRPC or messaging.

An important point in communication in modular monoliths seems to be using application messaging to transition easily into microservices. The difference is: in a modular monolith, messaging is in-process, purely in memory, with the mediator routing requests inside the same process. In microservices, messaging is distributed via a broker (e.g., RabbitMQ or Kafka) and involves network communication.


To summarize, a modular monolith is still physically a monolith, but it applies the logical architecture of microservice:

  • Modules must have clear bounded contexts, each with its own models, logic, and internal APIs.
  • Modules communicate primarily through in-memory method calls, but can optionally be made more “service-like” via messaging patterns (e.g., publish/subscribe or mediator pattern), which gives looser coupling and prepares modules for later extraction into real microservices.

The special property of a modular monolith is that, if done right, moving a module to a microservice is a smaller step (replace in-memory calls with network calls, give it its own DB, and deploy it separately).

Therefore, starting with a modular monolith seems like a good compromise for our project:

  • Simpler development and deployment: one codebase, one pipeline, easier debugging than microservices.
  • Future-proofing: we can stress-test later and extract bottleneck modules into microservices.

This way, we get the logical structure of microservices inside a simpler, single deployment, with a clear migration path, and option to experiment with microservices if time and resources allow. The main challenge seems to be keeping boundaries clear to avoid relapsing into classic monolith and making a possible transition to microservices smooth.

What do I need to do

(Write about any obstacles, or upcoming questions, that I may need to address)

  • Define bounded contexts for our LMS-modules, e.g., Scheduling, Notifications, File-Handling.
  • Research and decide on the communication style between modules.
  • Research data independence: how to structure the DB in a modular monolith.
  • (Optional) Plan for stress-testing to identify bottleneck modules as candidates for microservices.

Material

(Links, tutorials, or reference material)