Skip to content
Language:en Author:Laborci Gergely
architecture

Modulith: The Modular Monolith and Internal Contracts

Objectives

By the end of the lesson, you can explain what makes a real module boundary in a monolith, say what the modular monolith gains and does not gain compared with services, and recognise when it is a fitting choice.

Why is it needed?

The monolith is simple, but its internal boundaries easily blur (2.1). Microservices give strong boundaries, but at the cost of the network (3.3). The modular monolith (abbreviated in the course as modulith) is the middle road: it builds strong internal boundaries inside a single deployment unit, without a network.

What is it?

A modulith is a monolith in which the code is divided into units of responsibility, modules, so that modules reach each other only through a narrow, deliberately designed public interface, while their internal implementation and (ideally) their data are hidden. Its deployment unit is still one.

According to Martin Fowler, for most systems the good path is a monolith with well-defined internal modules, and the "monolith first" strategy also assumes that the monolith stays modular.

What makes a boundary real?

A folder name is only a label. There is a boundary when other modules cannot reach in arbitrarily:

  • the module provides a public interface with business meaning (for example "reserve a place", not "update a table row");
  • internal classes, the database connection and entities are not visible from outside;
  • the direction of dependencies is unambiguous and acyclic;
  • the module is a data owner: no other module writes directly into its data (1.3); database permissions, separate schemas or automated dependency checks can reinforce this.
flowchart LR
  A["Module A"] --> I["Public interface of B"]
  subgraph B["Module B"]
    I --> L["Rules"]
    L --> R["Internal data access"]
  end
  R --> DB[("Data of B")]

A shared transaction remains possible, but it must be a conscious decision: if an operation modifies the data of several modules in a single transaction, that guarantee has to be redesigned when splitting later.

By aspect

Aspect Modulith
Coupling Through a checked module interface; internals hidden
Deployability One shared unit, shared release
Scaling The whole unit together
Data management A shared database is possible, with owners per module
Consistency Local transaction possible
Failure handling One process, no network failure between modules
Team and operations cost Close to the monolith's; requires discipline and checking

What does it not give?

The modular structure does not give independent release, separate scaling or a separate failure boundary: the failure of the shared process affects everything. In return, it avoids the cost of network calls and the distributed data management between services. It is also a useful preparation: if a module later becomes an independent service, the direct internal accesses have already been removed, so the real calls and data needs are visible.

Fowler warns, however, that a good in-process interface is often a poor service interface. A module boundary is therefore not automatically a good service boundary.

When is it a fitting choice?

If the team handles a single release efficiently but the code boundaries are blurring more and more, or if service boundaries are still uncertain, the modulith is a cheap way to create strong boundaries. If, however, the parts truly need a separate release rhythm, separate scaling or a separate failure boundary, the limits of the modulith become tight (3.6).

Common misconceptions

Statement Clarification
"The modulith is only the antechamber of microservices." It can be a complete end state on its own.
"Module = folder." A module exists where access to the internals is controlled.
"Every module of a modulith gets its own database." Even with a shared database, a data owner per module can be designated.
"Modular structure reduces failure propagation." It is a shared process: a failure in one module can take down the others.

In short

  • A modulith is a single deployment unit with strong internal module boundaries.
  • The boundary is given by the public interface, the hidden internals, acyclic dependencies and the data owner.
  • There is no independent release, scaling or failure boundary.
  • It can be a final state on its own, and it can prepare a split.

Review questions

  1. What distinguishes a module from a simple folder?
  2. Why can it be a risk if one transaction modifies the data of several modules?
  3. In what situation would you choose a modulith instead of microservices?

Learning resources

Prerequisites (3)

  • monolith
  • cohesion and coupling
  • data owner

Teaches (1)

  • modular monolith