1. Courses
  2. System Design
  3. Distributed Systems
  4. Stop 3

35 minIntermediate

Monolith, modular monolith, microservices

"Should we split this into microservices?" comes up in almost every growing team. This lesson shows what each shape costs, why a well-built modular monolith is the right default for a small team, how to draw boundaries, and how to pull a service out later without a big rewrite.

By the end you can

  • Explain the difference between a monolith, a modular monolith and microservices in terms of deploys, code and data
  • List the real costs of microservices and say when they are worth paying
  • Draw module boundaries by business capability, with each module owning its data
  • Enforce boundaries in a NestJS codebase with module exports and dependency-cruiser
  • Plan a strangler fig extraction behind a proxy

The everyday picture

Think of a big supermarket with sections: grocery, fresh, household, pharmacy. There is one building, one set of doors, one cash line and one backstore. It is easy to run, and moving stock between sections is quick. But if nobody keeps order, the fresh team starts storing fish in the household aisle, and soon nobody knows who owns what.

Now think of a large shopping mall. Every shop has its own staff, its own cash counter, its own stockroom and its own opening hours. One shop can renovate without closing the others. But buying a phone in one shop and a cover in another means two queues, two payments and a walk between them, and the mall needs security, cleaning and rules that a single shop never needed.

A monolith is the supermarket. Microservices are the mall. A modular monolith is a supermarket with clear sections, a manager for each, and a rule that sections only take things from each other through the section counter.

Three shapes, defined by deploys and data

A is one codebase that you build and deploy as one unit, usually with one database. At Mangrove Market, the NestJS API behind the Store app is one, and yours probably is too. There is nothing wrong with that. Most successful products started this way, and many still run this way.

The problem is not the single deploy. It is the lack of walls inside. In a "big ball of mud" monolith, the order code reads the stock tables directly, the report code calls a private helper deep inside the payment code, and every change risks breaking something far away.

A is still one deploy and one database, but the code is split into modules with hard rules: a module exposes a small public API, keeps everything else private, and owns its own tables. Calls between modules are normal function calls, so they are fast, they cannot time out, and they can share one database transaction.

take the same modules and run each one as its own deployed service, with its own database, talking to others over the network. Each team can release on its own schedule and scale its service alone. In return, every call between modules becomes a network call that can be slow, fail, or leave you not knowing (see Failure is normal: timeouts, retries, backoff and circuit breakers).

Quick check

Your API is one deploy with one database. The code is split into folders that call each other only through one public service each, and each folder owns its own tables. What shape is this?

Monolith any code calls any code Orders Stock Payments all code, all tables Modular monolith calls go via public APIs Orders Stock Payments ordering inventory payments Microservices separate deploys Orders Stock Payments HTTP event own DB own DB own DB
The same three business modules in three shapes. In a monolith, code and tables are shared freely. A modular monolith is still one deploy, but modules call each other only through public APIs and each owns its own schema. Microservices split the deploy and the database too, so calls between modules cross the network.

Loading the rest of the lesson…