Modular monolith: Order without microservices
In short: An application that runs as one unit but is internally divided into clearly separated modules with fixed interfaces.
In more detail: A monolith is an application built and delivered as one program. That is not automatically bad: it is easy to develop, test and operate. Problems arise when everything is tied to everything else (“big ball of mud”). The modular monolith avoids this through clear boundaries inside, without the burden of distributed systems.
In Depth
Idea
The application is cut by business area, for example orders, catalogue, payment, users (see domain-driven design). Each module owns its data and offers others only a public interface. Access to internals and direct table access across module boundaries is forbidden.
Example of rules
src/
orders/ (public: index.ts, internal: db/, logic/)
catalog/
payment/
A linter or architecture test (for example ArchUnit, dependency-cruiser) makes the build fail if catalog accesses orders/logic instead of orders/index.
Advantages over microservices
A call between modules is a normal function call, without network, timeouts or distributed transactions. A database transaction can span several modules. There is only one pipeline and one deployment. For small teams this is often the wiser choice.
Keeping the path open
If the boundaries are clean, a module can later be extracted if needed and run as its own service, for example if it has to scale particularly strongly. The antidote to a bad monolith is usually better structure, not immediately more servers.
See also: Microservices, Domain-driven design, Hexagonal architecture