> “We should do microservices like Netflix!”
This is a sentence that developers have heard at least once, or even shouted themselves. From startups to large corporations, everyone dreams of transitioning to Microservice Architecture (MSA) to shed their massive ‘Legacy’ and become an agile organization. However, statistics show that a significant number of MSA transition projects fail or end up creating a ‘Distributed Big Ball of Mud’ that is even more complex than before.
Why is that? It’s because of ‘unprepared separation’.
Today, I’d like to talk in depth about ‘Modular Monolith’, an essential course to go through before moving to MSA, and an excellent architecture in itself.

1. β οΈ The Illusion and Reality of MSA: The Network Isn’t Free
First, we need to address why we are moving to MSA and what we are overlooking in the process.
Promises of MSA (Ideal)
- Agility: Independent deployment per service is possible.
- Scalability: Only services experiencing high traffic can be scaled out.
- Technological Autonomy: Team A can use Java, Team B can use Python.
Reality of MSA (Costs)
However, the costs to gain these advantages are enormous.
- Complexity of Network Calls: Function calls (In-process) change to network calls (RPC/HTTP). Failure handling, timeouts, and retry logic become necessary.
- Data Consistency Issues: As databases are split, transaction management becomes difficult. Complex techniques like 2PC or Saga patterns are enforced.
- Operational Overhead: Infrastructure complexity, such as Kubernetes, service mesh, and distributed tracing, increases exponentially.
The biggest problem is ‘incorrectly defined boundaries’.
If servers are physically separated without clearly defining the boundaries (Bounded Context) between services, ‘spaghetti communication’ with rampant API calls between services occurs. This results in the worst outcome: slower performance than a monolith and more difficult management.
2. π§© What is a Modular Monolith?
A Modular Monolith refers to an “architecture where the deployment unit is one (Monolith), but the internal structure is strictly modularized like microservices (Modular).”
- Physical Integration: Deployed as a single JAR/WAR file, a single binary.
- Logical Separation: Internal packages or modules are thoroughly separated. References between modules are strictly controlled, and direct access to each other’s internal database tables is prohibited.
Simply put, it’s like “roommates living under one roof, but with perfectly separate rooms.”
3. π‘ Why Should You Do ‘Modular Monolith’ First? (5 Key Reasons)
This is the core of this article. Why go through this stage instead of directly to MSA?
β Low Cost of Defining Boundaries (Context) π οΈ
The essence of MSA is ‘where to split’. However, in the early stages with insufficient domain understanding, it’s difficult to accurately define these boundaries.
- MSA: You split the code and deployed it on separate servers, only to realize later that the boundaries were wrong. The cost of merging them back or refactoring APIs is enormous.
- Modular Monolith: The code is within a single project. Boundaries can be modified simply by changing the package structure or using refactoring features (Move Class). The cost of reverting mistakes is almost zero.
β‘ Escape the Nightmare of ‘Distributed Transactions’ πΎ
The biggest headache of MSA is data consistency. If order and payment services are separated, they cannot be bound by a single transaction.
- Modular Monolith: Although modules are logically separated, they can physically use a single DB (though it’s good practice to separate schemas). If necessary, the powerful transaction features of RDBMS can be used as is. It’s not too late to split the DB later, after business logic has stabilized.
β’ 100% Support from Refactoring Tools β‘
Powerful IDEs like IntelliJ or Eclipse are optimized for code tracing and refactoring within a single project.
- “When you ask “Who calls this function?”, in an MSA environment, you have to grep through code or look at distributed tracing tools.”
- In a Modular Monolith, a single Cmd + Click reveals all call relationships. This makes a decisive difference in development productivity and maintaining code quality.
β£ Focus on ‘Modularity’ Without Infrastructure Complexity ποΈ
Implementing MSA requires significant DevOps engineering resources, such as Docker, k8s, Istio, and diversified CI/CD pipelines. You’re busy writing business logic, and then you have to worry about infrastructure.
A Modular Monolith has a simple deployment pipeline. It allows you to focus on practicing and implementing “good architecture (high cohesion, low coupling)” without infrastructure complexity.
β€ No Performance Loss (No Network Latency) π
No matter how fast the network, it cannot beat in-memory function calls.
In a Modular Monolith, communication between modules is a simple method call. There’s no serialization/deserialization (JSON Serialization) overhead, and no worries about network timeouts.
4. π How to Transition? (Practical Guide)
It’s not enough to just claim it’s a Modular Monolith. The following principles must be adhered to.
Step 1. Isolate Packages by Domain Unit
Abandon the existing layered architecture (e.g., Controller, Service, DAO grouped together). Instead, group packages by domain (feature).
- Bad: com.mycompany.controllers, com.mycompany.services
- Good: com.mycompany.order, com.mycompany.payment, com.mycompany.user
Step 2. Enforce Dependency Rules (Utilize ArchUnit)
If you just say “don’t reference,” no one will follow. For Java, use tools like ArchUnit to enforce architecture with test code.
> “The Order module cannot directly reference the Payment module.
> Communication must only occur through common interfaces (Event).”
Step 3. Logical Separation of Database
This is the most important. The moment you join tables from other modules, modularization fails.
- Separate schemas per module, or manage tables by adding prefixes to their names.
- If data from another module is needed, instead of joining, call that module’s API (service method) to retrieve it.
Step 4. Internal Communication via Interfaces
Communication between modules must strictly occur only through public interfaces. Implementations should be hidden as package-private.
5. π Conclusion: Modular Monolith Can Be a ‘Destination’
Even large tech companies like Shopify and Stack Overflow have either reverted from MSA to Modular Monoliths or maintain them as their core architecture.
A Modular Monolith is not just a ‘transitional stepping stone’ to MSA, but for many organizations, it can be ‘the most realistic and efficient final destination’.
Remember.
> “If you cannot cleanly separate modules even within a single process,
> splitting into microservices will unleash hell (Distributed Hell).”
>
Instead of building a k8s cluster right now, why not start by organizing our code’s import statements and drawing domain boundaries? That is the fastest shortcut to microservices.
Leave a Reply