Why We Start with a Modular Monolith

The short version
We start new business systems as a modular monolith: one deployable application, split inside into modules with clear boundaries. A module moves into its own service only when a specific requirement calls for it.
What goes wrong when services come first
Splitting a system into network services before the domain is understood makes the boundaries guesses, and every wrong guess now costs a network call, a versioned API and a deployment to fix. Teams end up spending their time on the plumbing between services instead of the product.
- Boundaries drawn too early. Services split by database table rather than by business capability end up calling each other for almost everything.
- Consistency becomes your problem. A change that used to be one database transaction turns into a sequence of calls that can half-succeed.
- Operations multiply. Each service needs its own deployment, health checks, logs and alerts, and a request that crosses several of them needs tracing before anyone can understand it.
What a modular monolith keeps
One codebase and one deployment keep transactions, refactoring and releases simple. The modules keep the discipline: each owns its data and logic and exposes a small, explicit contract to the others. Moving a boundary is an editor refactor and a test run, not an API migration.
How we keep the boundaries real
A boundary that only exists in a diagram erodes. We define each module's contract first, let other modules depend on that contract and not on its internals, and keep modules switchable so one that is not needed can be turned off. The same structure makes it cheap to lift a module out later.
When we do split a service out
When a requirement justifies the cost: a part of the system that must scale on its own, a different failure or security profile, or a separate team and release cycle. The contract already exists by then, so the move is mostly infrastructure work.
In our own products
RumuzePMO is a Laravel modular monolith with seven modules that can be enabled independently. It runs on FrankenPHP and Octane with Redis-backed queues and integrates several payment gateways.
Enjoyed this article? Share it with your network.