/ SPRING BOOT, MAVEN, SOFTWARE ARCHITECTURE

Multiple modules in Spring Boot apps

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:

modules deps
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

  1. Create a parent folder e.g. multiboot
  2. Create a POM with packaging pom in this folder. This will be the parent project.
  3. 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>
  4. 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.
  5. Add them as modules into the parent POM:
    multiboot/pom.xml
    <project...>
        <modules>
            <module>repo</module>
            <module>service</module>
            <module>web</module>
        </modules>
    </project>
  6. This is the resulting structure:
    Multi-module project structure
    Notice how it’s exactly the same as for legacy Spring projects structure
  7. Create a Maven wrapper:
    mvn -N io.takari:maven:wrapper
  8. Create a .gitignore with adequate data (or copy it from one of the module)
  9. 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.
  10. 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>
  11. Remove application classes from repo and service modules as they are not entry-points. Create a configuration class in both of them.
  12. In service, add repo as a dependency. In web, add service.
  13. 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.
Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a Software Architect with 15 years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with narrower interests like Software Quality, Build Processes and Rich Internet Applications. Currently working for an eCommerce solution vendor leader. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

Read More