A modern Java Spring project often has multiple parts that would make sense to separate, be able to build, and deploy separately. These can often be the core of a service, and a client for other services to use when they want to call ours. Another scenario is when we are creating multiple implementations for some interface, let's say we integrate multiple third parties (accounting software for example). We want our core service and logic to be third-party independent so we define interfaces between the partner-dependent implementations and the core logic. The core service, the interface package, and all the implementations can be separate modules.
This structuring makes our code nicely structured and easier to manage the modules independently. By defining the dependencies between our modules, we can enforce rules on what can be called avoiding creating spaghetti code and keeping everything nice and tidy. So how can we create these awesome modules?
Both Gradle and Maven support multi-module builds, in this article I will show you the Gradle version as I'm more familiar with that.
In my example, we will create a core service, a plugin interface, and 2 implementations of the interface that the core service uses.
- The service defines the core business logic, our program that we run
- The interface module has all the interfaces and model objects that the service can call to use the concrete implementations
- Implementations A and B are 2 implementations of the logic needed by the service
The project structure for this looks like the one below.
The root project is called
multi-modules, that one does not have a
build.gradle file, only a
settings.gradle with the following content, defining the submodules.
rootProject.name = 'multi-modules'
Dependencies between the modules can be defined in the
build.gradle files, in the dependencies section like this (from the service module):
Each submodule has its own
build.gradle file with all the configurations and settings for the given module. This can however lead to many duplications amongst the files. We want to use the Gradle
Java and other plugins in each module, we may also want to use the same external dependency in multiple modules. Adding this separately to each individual
build.gradle would be very hard to maintain and messy. Lucky for us we can create local Gradle plugins called convention plugins in the special
buildSrc folder that define these common dependencies, configurations, and build scripts. Afterward, we just have to add these custom plugins to all our modules where needed.
In our example the structure of the
buildSrc folder looks like this:
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
This convention plugin called
multimodule-config when applied will add the Java plugin, source, and target versions repositories test platform to use and a few dependencies to each subproject. I used this for all, for example in the service module I just add the lines
build.gradle and that is it. We can create a lot of these convention plugins to group specific dependencies and configurations like
java-conventions and so on.
There are more tricky situations, unfortunately. Let's say we have a Spring project and multiple modules require Spring boot dependencies. We want to use and specify the Spring Gradle plugin and all spring dependencies only once. If we try to add the Spring plugin to our convention plugin we will get an error.
id 'org.springframework.boot' version '2.7.0'
Will produce the following issue:
Invalid plugin request [id: 'org.springframework.boot', version: '2.7.0']. Plugin requests from precompiled scripts must not include a version number. Please remove the version from the offending request and make sure the module containing the requested plugin 'org.springframework.boot' is an implementation dependency
If we simply omit the version number the build won't find the plugin and fail. What we have to do is to add the Spring boot plugin as an implementation dependency in the
build.gradle file for the
buildSrc folder. Don't forget to add the maven central repository too!
And this is how you create a Spring Boot Gradle multi-module project!
Examples from this article can be found here:
Further info on Gradle multi-module usage: