Why: the goal or problem
Features interfere, but separate services add unwanted operating work. You need boundaries within one deployment.
How: work toward a solution
- Fictional app v1: one team deploys Catalog, Library and Billing together; A17 has no tag.
- Library adds tag storage in its owned tables. Deploy app v2; Billing behavior stays unchanged. One database holds module-owned tables; direct cross-module access is forbidden.
- Library calls Catalog’s API in-process, then writes
travel. A lookup error leaves no tag; retry after recovery.
Illustrative example
Internal contracts, shared release
Reading app · one team · A17 has no tag
Change: add a Library tag; Billing behavior stays unchanged.
Deployment unit · 1
App v1 → App v2Catalog
APIArticle lookupOwned internalsCatalog-owned tablesLibrary
Tag feature addedAPISet A17 tag: travelOwned internalsLibrary-owned tables + tag fieldBilling
APIBehavior unchangedOwned 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.
- Lookup succeeds → write travel → A17 has tag travel.
- 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.
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.
Discuss this idea
Comments are shared across languages. GitHub login required to post.