PostHole
Compose Login
You are browsing us.zone2 in read-only mode. Log in to participate.
rss-bridge 2026-02-25T12:40:36+00:00

Migrating to Modular Monolith using Spring Modulith and IntelliJ IDEA

As applications grow in complexity, maintaining a clean architecture becomes increasingly challenging. The traditional package-by-layer approach of organizing code into controllers, services, repositories, and entities packages often leads to tightly coupled code that’s hard to maintain and evolve. Spring Modulith, combined with IntelliJ IDEA’s excellent tooling support, offers a powerful solution for building well-structured modular […]


IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin

Follow

  • Follow:

Download

IntelliJ IDEA

Migrating to Modular Monolith using Spring Modulith and IntelliJ IDEA

Siva Katamreddy

As applications grow in complexity, maintaining a clean architecture becomes increasingly challenging. The traditional package-by-layer approach of organizing code into controllers, services, repositories, and entities packages often leads to tightly coupled code that’s hard to maintain and evolve.

Spring Modulith, combined with IntelliJ IDEA’s excellent tooling support, offers a powerful solution for building well-structured modular monoliths.

In this article, we will use a bookstore sample application as an example to demonstrate Spring Modulith features.

If you are interested in building a Modular Monolith using Spring and Kotlin, check out Building Modular Monoliths With Kotlin and Spring

1. The Problem with Monoliths and Package-by-Layer

Many Spring Boot applications are organized by technical layer rather than by business capability. A typical layout looks like this:

bookstore
|-- config
|-- entities
|-- exceptions
|-- models
|-- repositories
|-- services
|-- web

This package-by-layer style causes several problems.

The Code Structure Doesn’t Express What the Application Does

Everything Tends to Become Public

In a layer-based layout, types in one package are often used from many others. To allow that, classes are made public, which effectively exposes them to the whole application. There is no clear “public API” per feature, and hence anything can depend on anything.

Tight Coupling and Spaghetti Code

With no explicit boundaries, services and controllers from different features depend on each other’s internals. For example, order logic might call catalog’s ProductService directly or reuse internal DTOs. Over time this turns into a tightly coupled “big ball of mud” where changing one feature risks breaking others.

Fragile Changes

Adding or changing a feature often forces you to touch code in repositories, services, and web at once, with no clear “module” to test or reason about. Refactoring becomes risky because the impact is hard to see.

In short: package-by-layer encourages a single, undivided monolith with weak boundaries and unclear ownership. Spring Modulith addresses this by turning your codebase into an explicit set of modules with clear APIs and enforced boundaries.

2. What Benefits Spring Modulith Brings

Spring Modulith helps you build modular monoliths: one deployable application, but with clear, domain-driven modules and enforced structure.

Explicit Module Boundaries

Modules are direct sub-packages of your application’s base package (e.g. com.example.bookstore.catalog, com.example.bookstore.orders). Spring Modulith treats each as a module and checks that:

  • Other modules do not depend on internal types unless they are explicitly exposed.
  • There are no circular dependencies between modules.
  • Dependencies between modules are declared (e.g. via allowedDependencies), so the architecture stays intentional.

Clear Public APIs

Each module can define a provided interface (public API): a small set of types and beans that other modules are allowed to use. Everything else is internal. This reduces coupling and makes it obvious how modules interact.

Event-Driven Communication

Spring Modulith encourages events for cross-module communication (e.g. OrderCreatedEvent). It provides:

  • @ApplicationModuleListener for module-aware event handling.
  • Event publication registry (e.g. JDBC) so events can be persisted and processed reliably.
  • Externalized events (e.g. AMQP, Kafka) to integrate with message brokers and other applications.

This keeps modules loosely coupled and makes it easier to later extract a module into a separate service.

Testability

You can test one module at a time with @ApplicationModuleTest, controlling which modules and beans are loaded. You mock other modules’ APIs instead of pulling in the whole application, which speeds up tests and keeps them focused.

Documentation and Verification

Spring Modulith can:

  • Verify modular structure in tests via ApplicationModules.of(...).verify().
  • Generate C4-style documentation from the same model.

So the documented architecture and the actual code stay in sync.

Gradual Migration Path

You can introduce Spring Modulith into an existing Spring Boot monolith step by step: first refactor to package-by-module, then add the Spring Modulith dependencies and ModularityTest, and fix violations one by one. You don’t need to rewrite the application.

3. How to Add Spring Modulith to a Spring Boot Project

Add the Dependencies

Use the Spring Modulith BOM and add the core and test starters:

<properties>
<spring-modulith.version>2.0.3</spring-modulith.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-bom</artifactId>
<version>${spring-modulith.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- other dependencies -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-core</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Enable IntelliJ IDEA Support

Spring Modulith support is bundled in IntelliJ IDEA with the Ultimate Subscription and is enabled by default once the Spring Modulith dependencies are on the classpath.

To confirm the plugin is enabled:

  • Open Settings (Ctrl+Alt+S / Cmd+,).
  • Go to PluginsInstalled.
  • Search for Spring Modulith and ensure it is checked.

You can then use module indicators in the project tree, the Structure tool window, and Modulith-specific inspections and quick-fixes.

Add a Modularity Test

Add a test that verifies your modular structure so that violations are caught in CI:

package com.sivalabs.bookstore;

import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;

class ModularityTest {
static ApplicationModules modules = ApplicationModules.of(BookStoreApplication.class);

@Test
void verifiesModularStructure() {
modules.verify();

After refactoring to package-by-module, this test will fail until all boundary and dependency rules are satisfied. Fixing those failures is the main migration work.

4. Converting a Monolith into a Modulith: Refactoring to Package-by-Module

Let’s see how we can convert a monolith application into a modular monolith one step at a time.

Step 1: Reorganize to Package-by-Module

Move from layer-based packages to module-based (package-by-module) packages. Each top-level package becomes a module.

Target structure (example):

bookstore
|- config
|- common
|- catalog
|- orders
|- inventory

Practical steps:

[...]


Original source

Reply