Why a Modular Monolith? Architectural Decisions and Scalability
In this post we will tackle one common decision in software development these days: Monolith or microservices architecture? This decision…

Why a Modular Monolith? Architectural Decisions and Scalability
In this post we will tackle one common decision in software development these days: Monolith or microservices architecture? This decision always comes up when building software from scratch, so we will discuss why we chose one over the other. By the end of this post, you will understand the final decision, its pros and cons, and the reasoning behind it.
This post is part of a series where we build a simplified credit and lending platform using Spring Boot, designed to illustrate architectural concepts in a practical and approachable way. In our [previous posts](http://designed our architecture using Clean Architecture and DDD principles) we designed our architecture using Clean Architecture and DDD principles.You can find the full project on GitHub.
In this post we will cover:
- Why a Modular Monolith?
- How to scale later.
- Conclusion
Why a Modular Monolith?
To understand our decision, we need to go through three important concepts:
- Modular Monolith: Is a single, deployable application structured into distinct, independent modules, each representing a specific business domain or capability. It bridges the gap between a traditional monolith (no modularization) and microservices.
- Traditional Monolith: A traditional monolith is a software architecture style in which there is no modularization — everything is packaged in a single deployable artifact.
- Microservices: Microservices are the other extreme, where everything is modularized and each module is a separate deployable artifact.

Now, the decision. With a Traditional Monolith, although it is easier to write code in a fresh project and to deploy the application — since it produces a single artifact — giving maintenance to the code later becomes increasingly difficult. Making changes according to business needs is harder because everything is tangled together, writing tests can become extremely difficult, and the learning curve for someone new to the project also increases significantly. All of this becomes more manageable with a Modular Monolith. The codebase is organized in a more intuitive way, making it easier to locate features, create tests, add new features, or modify existing ones. The learning curve for someone new to the project will also be lower than with a Traditional Monolith — and the Clean Architecture and DDD principles we covered in our ***previous post*** contribute directly to this clarity.
On the other extreme are Microservices — separate, independently deployable artifacts. For someone new to the codebase, adding a feature to the credit decision system means only changing something within the credit decision microservice. This results in a lower learning curve and easier testing. However, it also introduces more failure points, since you will have multiple artifacts to monitor in production. This operational complexity can be too much for a small team with a limited budget.
So what should we do? Start small and plan to grow — that is my best answer. The pros of a Traditional Monolith become their own cons as the project grows. That is why we are starting with a Modular Monolith: we need its simplicity and lower operational complexity to begin, but we will also plan for its future growth. In our case, we know that user demand will be low at the start, we have a small team to handle maintenance, and we are working with a fixed budget — Microservices would not fit our initial needs.

Small/Early Stage Project

Growing / Mature Project
Now that we understand why we chose a Modular Monolith, let’s look at how we plan to scale from it.
How to scale later
If you remember the current structure from our ***previous post***, you know that we have already separated our domains into modules in our project. Each module has its own domain, application, and infrastructure layers — and that is the key. With this structure, each module can become its own Microservice when the time comes.
For our domain events, we will use ApplicationEventPublisher in our Modular Monolith to communicate between domains. Being honest, we will also have direct Java calls handling data between modules. Even though strict Event-Driven Architecture purists would recommend against it, in practice — and this is my opinion — there is too much to handle for a small project with a small team and a fixed budget to justify the added complexity of turning every Java call between domains into an event. We covered all of these constraints earlier, and they directly influenced this decision.
We also have in mind that not all modules need to scale equally or at the same time. We need to identify the bottlenecks and make the decision based on real traffic data — not every module will have the same demand. Once we have identified the module that needs to become a Microservice, we will create a separate project for it, replace the in-process events with Kafka messages and the direct calls with HTTP calls, and deploy it independently.

Coupling Trade-off
As you may have noticed, with HTTP calls we still have a coupled architecture between our Microservice and the modules in our Modular Monolith — and that is a well-thought-out decision. You will face this kind of decision where you have to choose between the complexity of making everything fully event-driven, or the lower complexity but coupled nature of HTTP calls. For our project, and with all the considerations I presented before, this trade-off is manageable.
Conclusion
In this post I presented how we are going to scale our project later — moving from a module in a Modular Monolith to an independent Microservice. I covered, still theoretically — in a future post we’ll do it with code— how we are going to do this and how our Modular Monolith helps us achieve it. We also discussed the trade-off between making everything fully event-driven, as strict Event-Driven Architecture purists would recommend, versus the coupled nature of using HTTP calls between our Modular Monolith and the extracted Microservice.
메타데이터
- post_id
- e5065dbd4f7a
- slug
- why-a-modular-monolith-architectural-decisions-and-scalability-e5065dbd4f7a
- url
- https://medium.com/@emenaa/why-a-modular-monolith-architectural-decisions-and-scalability-e5065dbd4f7a
- canonical_url
- https://medium.com/@emenaa/why-a-modular-monolith-architectural-decisions-and-scalability-e5065dbd4f7a
- author_url
- https://medium.com/@emenaa
- status
- ok
- fetched_at
- 2026-06-11 05:11:55