Why: the goal or problem

Imagine a hiking app where walkers save guides and buy extras. Weekend tags risk changing billing code. The team wants clear ownership within one release, without separate services.

How: work toward a solution

  1. One team ships Catalog, Library and Billing together; saved Hiking Guide has no tag.
  2. Add tags to Library-owned tables; deploy app v2 with unchanged Billing. One database; cross-module table access is forbidden.
  3. Library calls Catalog’s API in-process, then writes weekend. Failed lookup leaves no tag; retry after recovery.
Internal contracts, shared release

Illustrative example

Internal contracts, shared release

Hiking app · one team · Hiking Guide has no weekend tag

Change: add a Library tag; Billing behavior stays unchanged.

Deployment unit · 1

App v1 → App v2
  1. Catalog

    APIArticle lookup
    Owned internalsCatalog-owned tables
  2. Library

    Tag feature added
    APISet Hiking Guide tag: weekend
    Owned internalsLibrary-owned tables + tag field
  3. Billing

    APIBehavior unchanged
    Owned internalsBilling-owned tables

Library → Catalog API · In-process call

No direct access to another module's tables. Calls enter through its API.

One shared database; separate table ownership. Library changes its storage inside the common release.

  1. Lookup succeeds → write weekend → Hiking Guide has tag weekend.
  2. Lookup fails before writing → no tag. Report failure; retry after recovery.

A monolith subtype: one release and shared process failures. Enforce API boundaries with code rules and tests; folders alone cannot protect them.

Ownership boxes describe access rules, not database-machine counts.

Module APIs protect source and data ownership. They still share this release and process failure boundary.

What: the concept

A modular monolith combines one deployment with deliberate internal boundaries. Module APIs protect owned internals. It is still a monolith. Fowler

Enforce boundaries beyond folders; releases and process failures remain shared. Modules can use hexagonal ports.