Modular Monolith is the new Microservices
You probably don’t need these microservices, which are so difficult to deploy. The monolith, provided it’s modular, is right for you.

Talking about modular monoliths at the end of 2025 is a bit like showing up late to the party. We’ve all struggled with trying to run three microservices on managed Kubernetes and cried every month when we got the bill. The concept of the modular monolith, with its refreshing pragmatism, has caught on. However, its practical implementation deserves closer attention.
Contexts deployed within the same shell
Let’s start by reviewing what a modular monolith is:
-
The modular monolith differs from the spaghetti monolith in its clear structure based on bounded contexts. This means that a true Strategic DDD approach was carried out beforehand. We know what we’re getting into.
-
At the same time, however, the modular monolith retains the ease of deployment of the monolith, since the different contexts are deployed within a single “shell.” The immediate consequence, however, is that redeploying one context means redeploying all contexts (you can’t have your cake and eat it too).
The beauty of the modular monolith is that it doesn’t lock you into a specific future: there will always be time to switch to microservices if the need arises. When you’re Google or Netflix and you’re facing real scalability issues 😉
Implementation: 4 Essential Principles
However, having said that, we haven’t really said much. From experience, implementing a modular monolith raises questions. Here are four aspects that I find particularly important:
-
Isolate the domain within each context, using either Hexagonal Architecture or Functional Core/Imperative Shell. Thus, your monolith is a juxtaposition of neat little hexagons neatly arranged side by side, even if the HTTP API and infrastructure are shared.
-
If the different contexts require the same type of database (relational, document, graph, key-value, etc.), the database can be shared. However, we’ll want to maintain the organization by context within it. For an SQL database, this means creating one schema per context.
-
And a context is strictly prohibited from reading from or writing to the database in its buddy’s schema. Instead, it properly calls the API exposed by the context in question. Just like with an object, directly accessing a context’s data would be a violation of responsibility (“tell, don’t ask”).
-
When I say “API,” you might consider actually invoking the HTTP API, as opposed to in-memory function calls. Admittedly, this introduces latency, but it ensures that requests pass through all the validation layers of the HTTP controllers and facilitates the subsequent transition to microservices. It also provides an opportunity for better load balancing when you deploy multiple application instances of the same monolith.
That said, we’ve assumed so far that contexts communicate with one another via direct calls. I advise against doing this, because the transition to microservices, if it were to happen one day, would be downright painful. So what should we do instead?
What about communication between contexts?
The deployment model says nothing about how contexts communicate with each other. Just as if they were deployed as microservices, you can use an orchestrator (a central point that knows all the contexts) or opt for event-driven communication via a message bus to achieve better decoupling between contexts.
Thus, in an Event-Driven Architecture, each context listens for and records the information it will need for future processing. Later, when queried via its API, the context has all the necessary information to operate completely autonomously, regardless of whether other contexts are operational or not (in the case of microservices). The only calls it will make are to services, such as generating a PDF or sending an email. And this context, in turn, emits new events related solely to its own responsibility. We refer to this as choreography and sometimes as an event “cascade” when we observe the chain reactions between contexts.
This approach also has its challenges, since transactions (and therefore rollbacks) must be managed at scale. But in my experience, it’s beneficial even for medium-sized systems (a few dozen contexts).