Spring Boot is a huge success, perhaps even more so than its inceptors hoped for. There is a lot of documentation, blog posts, and presentations on Spring Boot. However, most of them are aimed toward a feature, like monitoring or configuring. Few - if any of them, describe real-world practices.

In particular, demos are mainly based on very simple apps, such as the Spring Pet Clinic. On the other hand, Spring legacy apps are usually designed into multiple modules. Not every app can, nor should, be designed as a micro-service. It doesn’t help that the Spring Initializr service doesn’t propose a multi-modules option.

In this post, I’d like to highlight how to design a Spring Boot having multiple modules. This an example of such design:

Let’s be honest, it’s not the best design ever. However, it’s the most widespread one regarding Spring projects. Thus it makes for a great example, and can be easily adapted to one’s own.

Basic setup Create a parent folder e.g. multiboot Create a POM with packaging pom in this folder. This will be the parent project. Set its parent to the Spring Boot starter parent: multiboot/pom.xml <project...> <parent> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-parent </artifactId> <version> 2.0.0.RELEASE </version> <relativePath/> <!-- lookup parent from repository --> </parent> </project> Use the Spring Initializr (either from the website or from the IDE) to create different Spring Boot modules under the parent folder: e.g. repo , service and web . Set relevant dependencies for each of them. If using IntelliJ IDEA, this is quite straightforward with File New Module and then choosing Spring Initializr from the menu. Add them as modules into the parent POM: multiboot/pom.xml <project...> <modules> <module> repo </module> <module> service </module> <module> web </module> </modules> </project> This is the resulting structure: Notice how it’s exactly the same as for legacy Spring projects structure Create a Maven wrapper: mvn -N io.takari:maven:wrapper Create a .gitignore with adequate data (or copy it from one of the module) In the modules POM: Change the parent coordinates to the parent POM coordinates instead of spring-boot-starter-parent

Move section properties to the parent POM

Move section build to the parent POM, nesting plugins into pluginManagement

Optional: clean unnecessary tags, such as packaging (default is jar ), groupId and version (inherited from parent), name and description . What’s left in the modules POM should be quite concise, containing only parent , artifactId and dependencies : <project...> <modelVersion> 4.0.0 </modelVersion> <parent> <groupId> ch.frankel.blog.multiboot </groupId> <artifactId> parent </artifactId> <version> 1.0-SNAPSHOT </version> </parent> <artifactId> repo </artifactId> <dependencies> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-data-jpa </artifactId> </dependency> <dependency> <groupId> org.jetbrains.kotlin </groupId> <artifactId> kotlin-stdlib-jdk8 </artifactId> </dependency> <dependency> <groupId> org.jetbrains.kotlin </groupId> <artifactId> kotlin-reflect </artifactId> </dependency> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-test </artifactId> <scope> test </scope> </dependency> </dependencies> </project> Remove application classes from repo and service modules as they are not entry-points. Create a configuration class in both of them. In service , add repo as a dependency. In web , add service . Inject the Repository into the Service , and the Service into the Controller That should be it!

The problem At this point, the project seems to be configured adequately. However, launching the project will probably result in the following: *************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of constructor in ch.frankel.blog.multiboot.web.PersonController required a bean of type 'ch.frankel.blog.multiboot.service.PersonService' that could not be found. Action: Consider defining a bean of type 'ch.frankel.blog.multiboot.service.PersonService' in your configuration. The problem comes from the way component scanning is handled by Spring Boot. Only the package where the @SpringBootApplication -annotated class resides and its children will be scanned. Chances are you designed a "classical" package structure with every module in its own package: e.g. ch.frankel.blog.multiboot.web , ch.frankel.blog.multiboot.service and ch.frankel.blog.multiboot.repo . With the main application class in the first package, other packages won’t be scanned. Hence, autowiring won’t take place and the above failure will happen.

The options There are basically 2 options to resolve this problem: Move the main class Obviously, moving the application class to the root package e.g. ch.frankel.blog.multiboot will solve the issue easily. However, it breaks the package structure design. Configure the component scan location The alternative is to tell Spring Boot in which locations it should scan. For regular beans, it’s quite straightforward: @SpringBootApplication ( scanBasePackageClasses = [ ServiceConfig :: class ]) However, entities and JPA repositories require a dedicated annotation: @EntityScan ( basePackageClasses = [ RepoConfig :: class ]) @EnableJpaRepositories ( basePackageClasses = [ RepoConfig :: class ])

Conclusion Designing a multi-modules Spring Boot application requires a lot of manual setup compared to a standard Spring Boot app. But no more than a classical Spring app without Boot. Modules are a great way to enforce boundaries between chunks of not-so-related code. Plus, with Java 9, they can map to Java 9 modules. Finally, they can be a first step toward micro-services - or even render them unnecessary in one’s context.

The complete source code for this post can be found on Github in Maven format.