Multiple Spring Boot applications in the same project
Tags: java, springboot, demo, architecture
Published: 2021-12-05 16:59:05

I frequently use the [Spring Boot](https://spring.io/projects/spring-boot) framework in my demos. The latest one is no different. It shows how to achieve <abbr title="Command Query Responsibility Segregation">CQRS</abbr> using two different code paths:

* the command part is implemented via [Spring Data JPA](https://spring.io/projects/spring-data-jpa)
* the query part via [jOOQ](https://www.jooq.org/)

My use case is a banking application that offers a REST layer allowing clients to call any parts. Demoing the query part is easy enough with `curl` as the URL is not complex:

```bash
curl localhost:8080/balance/123          // 1
```

1. Query the balance of the account `123`

On the other hand, creating a new operation, _e.g._, a credit, requires passing data to `curl`. While it's feasible to do that, the payload's structure itself _is_ complex as it's part JSON. Hence, it's a risk to use `curl` to demo the command part in front of a live audience. I tend to avoid unnecessary risks, so I thought about a few alternatives.

My first idea was to prepare the command in advance, to copy-paste during the demo. I wanted to have a couple of different operations, so I'd have to:

* Either prepare a single command, paste it and change it before running it
* Or prepare all the different commands

To me, both were too awkward.

Another option was to create another `@SpringBootApplication` annotated class in the same project:

```java
@SpringBootApplication
public class GeneratorApplication {

    @Bean
    public CommandLineRunner run() {
        var template = new RestTemplate();
        return args -> {
            var operation = generateRandomOperation();            // 1
            LongStream.range(0, Long.parseLong(args[0]))          // 2
                .forEach(
                    operation -> template.postForObject(          // 3
                        "http://localhost:8080/operation",
                        operation,
                        Object.class));
        };
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(GeneratorApplication.class)
                .run(args);
    }
}
```

1. Generate a random `Operation`, somehow
2. Get the number of calls from the argument
3. Call the URL of the main web application

When I launched this application after the other web one, it failed. There are two reasons for that:

1. Both applications share the same Maven POM. As the `spring-boot-starter-web` is on the classpath, the generator application tries to launch Tomcat. It fails because the first application did bind the default port.
2. Spring Boot relies on component scanning by default. Hence, the web application scans the generator application and its declared beans and creates them. It's possible to redefine some of the beans this way. However, the webapp also creates the `CommandLineRunner` bean above. It thus "posts to itself" while its server is not ready yet.

The most straightforward solution is to move each application's classes in their own dedicated Maven module. You need to create a POM in each module with only the necessary dependencies. Plus, I need to use a couple of classes in the runner from the webapp. While I could duplicate them in the other module, it's extra work and complexity.

To prevent classpath scanning, we move each application class into its package. Note that it doesn't work when packages have a parent-child relationship: they must be siblings.

To create beans of specific classes, we need to rely on particular annotations depending on their nature:

* For JPA entities, `@EntityScan`, pointing to the package to scan
* For JPA repositories, `@EnableJpaRepositories`,  pointing to the package to scan
* For other classes, `@Import` points to the classes to generate bean from

The final step is to prevent the generator application from launching the webserver. You can configure it when launching the application.

```java
@SpringBootApplication
@EnableJpaRepositories("org.hazelcast.cqrs")                      // 1
@EntityScan("org.hazelcast.cqrs")                                 // 2
public class GeneratorApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(GeneratorApplication.class)
                .web(WebApplicationType.NONE)                     // 3
                .run(args);
    }

    // Command-line runner
}
```

1. Scan for JPA repositories
2. Scan for JPA entities
3. Prevent the web server from launching

It works as expected, and we can finally reap the benefits of the setup.

For real-world applications, you'll probably create a self-executing JAR. You'll need to develop a way to configure the main class during the build, one for each application. I think it's better not to do it and keep this as a demo hack.

* [Create a Non-web Application](https://docs.spring.io/spring-boot/docs/2.6.x/reference/htmlsingle/#howto.application.non-web-application)
* [Use Spring Data Repositories](https://docs.spring.io/spring-boot/docs/2.6.x/reference/htmlsingle/#howto.data-access.spring-data-repositories)
* [Separate @Entity Definitions from Spring Configuration](https://docs.spring.io/spring-boot/docs/2.6.x/reference/htmlsingle/#howto.data-access.separate-entity-definitions-from-spring-configuration)
* [Importing Additional Configuration Classes](https://docs.spring.io/spring-boot/docs/2.6.x/reference/htmlsingle/#using.configuration-classes.importing-additional-configuration)

_Originally published at [A Java Geek](https://blog.frankel.ch/multiple-spring-boot-apps-same-project/) on December 5<sup>th</sup>, 2021_