Building Modular Monoliths with Independent Data Sources in Spring Boot

ganesan arunachalam
3 min readApr 19, 2024

--

Terminology:

Deployment Unit: Typically an Independent Service, Microservice, or an independent code repository

Module: Refers to a Java Package or Subdomain

In the realm of software architecture, the debate between monoliths and microservices has been ongoing. However, there exists a middle ground: the modular monolith. In this article, we explore the concept of modular monoliths with independent data sources within the context of Spring Boot.

What is a Modular Monolith?

A service is constructed with several subdomains, often forming a single deployment unit for these components. However, it retains the potential to evolve into multiple independent services or deployments more rapidly than if built from a traditional monolithic codebase.

What makes a monolith modular?

What sets a modular monolith apart is its adherence to certain architectural principles. One such principle is the separation of concerns, which ensures that each module is responsible for a specific aspect of the system’s functionality. This promotes maintainability and facilitates independent development within each module. Encapsulation is another crucial aspect, ensuring that modules interact with each other through network calls, rather than direct dependencies.

How do we do that?

Wouldn’t the team adhere to conventions and guidelines after discussing them in a meeting and documenting them somewhere? Certainly, if you’re working solo. However, in reality, it involves a diverse group of individuals with varying skill levels, backgrounds, and perspectives collaborating to achieve a common goal. Therefore, it’s imperative to establish a systematic approach for validating the architecture.

Achieving modularity requires more than just architectural design; it necessitates a disciplined approach to development. One way to enforce modularity is through the use of architectural tests, such as ArchUnit tests in Java-based projects. These tests verify that each module adheres to predefined architectural constraints, preventing unwanted dependencies and promoting isolation.

We have the option to create ArchUnit tests to ensure that one subdomain does not access or rely on another subdomain, thus truly making them independent packages. If a subdomain cannot be accessed like any other regular class, data movement would only be possible through network calls.

Is that enough?

In most scenarios, this holds true. Nevertheless, there might be instances where the data access pattern varies across subdomains, or we anticipate that a subdomain will eventually become a separate deployment unit. In such cases, it becomes logical to implement a distinct data source for each module. This ensures that transitioning a package into a separate deployment unit, complete with its own data source and, if necessary, schema migration files, is a straightforward process.

Practical Example:

Consider an application for managing school transportation. This application comprises several modules, including school and student management, driver management, and trip management. While these modules are interconnected, they operate autonomously within the monolith, allowing for independent development and deployment. As the application grows, each module can be scaled and evolved independently, ensuring flexibility and agility.

https://github.com/ganesanarun/spring-modular-monolith.git (updates coming soon)

Conclusion:

In the ever-evolving landscape of software development, the modular monolith emerges as a compelling alternative to traditional monolithic architectures and microservices. By combining the benefits of both approaches, developers can build robust, scalable systems that are easy to develop, deploy, and maintain. With careful architectural design and disciplined development practices, modular monoliths with independent data sources offer a pragmatic solution for modern software projects, especially in the Spring Boot ecosystem.

--

--