Reactive MongoDB with multi-database within a single Spring Boot application
Tags: java, springboot, reactor, mongodb
Published: 2023-09-24 08:22:45

## Disclaimer

This article represents my perspective on this solution, thus any suggestions, fixes, or discussions will be highly appreciated.

## The short story

Let's assume that in any circumstance you have a few databases you need to use to catch/write data within a single Spring Boot application. It can be  a result of legacy approach or a result of DBA works etc.
Spring Boot will do it easily for you. Let's start.

## The implementation

In many real-world scenarios, Spring Boot applications need to interact with multiple databases. Whether it's due to legacy systems, the need for polyglot persistence, or other reasons, Spring Boot simplifies the task of managing multiple MongoDB databases, even in a reactive environment. In this article, we'll walk you through the implementation steps to seamlessly support multiple MongoDB databases within a single Spring Boot application using reactive programming.

_Step 1: Configuration setup_

Begin by configuring your Spring Boot application to connect to the MongoDB databases. In your `application.properties` (or `application.yml`) file, define the connection details for each database, specifying database names, connection URIs, and other necessary properties. To ensure clarity, focus on the essential MongoDB configuration properties. 

```properties
spring.data.mongodb.databaseOne.database=databaseOne
spring.data.mongodb.databaseTwo.database=databaseTwo
spring.data.mongodb.url=mongodb://localhost:27017/test
spring.autoconfigure.exclude=\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration
```

In order to use multiple database approach we need to disable MongoDB autoconfigurations by adding the property `spring.autoconfigure.exclude`.

_Step 2: Creating reactive MongoDB Repositories_

To interact with each MongoDB collection, create Spring Data MongoDB repositories for each one. In your project structure, organize these repositories according to the respective database names, enhancing code readability.


```java
package com.demo.repository.database_one;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

public interface CollectionOneRepository extends ReactiveMongoRepository<CollectionOne, String> {

}
```
```java
package com.demo.repository.database_two;

import org.springframework.data.mongodb.repository.ReactiveMongoRepository;

public interface CollectionTwoRepository extends ReactiveMongoRepository<CollectionTwo, String> {

}
```

_Step 3: Configuration Properties class_

Simplify your code and improve maintainability by creating a configuration properties class that stores configurations per prefix. This step ensures better code organization and allows for additional validations.


```java
package com.demo.config.mongo;

import lombok.Data;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.validation.annotation.Validated;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

@Data
@Validated
@Primary
@ConfigurationProperties(prefix = "spring.data.mongodb", ignoreUnknownFields = false)
public class BaseMongoProperties {

    @NotNull private MongoProperties databaseOne;
    @NotNull private MongoProperties databaseTwo;
    @NotNull @NotEmpty private String uri;
    
}
```

Pay attention it need to be marked as a `@Primary` configuration, otherwise Spring Boot will throws exception about 2 existing properties. We would like to override these properties by our custom properties.

_Step 4: MongoDB Template configuration_

Create a main configuration class for MongoDB templates. This class defines two MongoDB templates, one for each database, using the ReactiveMongoTemplate. With this approach we can create one MongoDb client only and reuse it for different MongoTemplates, each for a certain database.

```java
package com.demo.config.mongo;

import com.mongodb.*;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;

@Configuration
@EnableConfigurationProperties(BaseMongoProperties.class)
public class BaseMongoConfig {

    protected static final String DATABASE_ONE_MONGO_TEMPLATE = "databaseOneMongoTemplate";
    protected static final String DATABASE_TWO_MONGO_TEMPLATE = "databaseTwoMongoTemplate";

    @Bean(name = {DATABASE_ONE_MONGO_TEMPLATE})
    public ReactiveMongoTemplate databaseOneMongoTemplate(MongoClient mongoClient, BaseMongoProperties mongoProperties) {
        return new ReactiveMongoTemplate(mongoClient, mongoProperties.getDatabaseOne().getDatabase());
    }

    @Bean(name = {DATABASE_TWO_MONGO_TEMPLATE})
    public ReactiveMongoTemplate databaseTwoMongoTemplate(MongoClient mongoClient, BaseMongoProperties mongoProperties) {
        return new ReactiveMongoTemplate(mongoClient, mongoProperties.getDatabaseTwo().getDatabase());
    }

    @Bean
    public MongoClient mongoClient(BaseMongoProperties mongoProperties) {
        return MongoClients.create(getMongoClientSettings(mongoProperties));
    }

    private MongoClientSettings getMongoClientSettings(BaseMongoProperties mongoProperties) {
        return MongoClientSettings.builder()
                .applyConnectionString(new ConnectionString(mongoProperties.getUri()))
                .build();
    }

}
```

_Step 5: Repository Configuration_

We inform Spring Boot about the repositories associated with each MongoDB template by importing `ReactiveMongoRepositoriesRegistrar` will register our per database repositories and associate them with the dedicated `MongoTemplate`. Create configuration classes for each database, specifying the base package and referencing the correct MongoDB template.

```java
package com.demo.config.mongo;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;

import static com.demo.config.mongo. BaseMongoConfig.DATABASE_ONE_MONGO_TEMPLATE;

@Configuration
@EnableReactiveMongoRepositories(
        basePackages = {"com.demo.repository.database_one"},
        reactiveMongoTemplateRef = DATABASE_ONE_MONGO_TEMPLATE
)
public class DatabaseOneMongoConfig {

}
```
```java
package com.demo.config.mongo;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;

import static com.demo.config.mongo.BaseMongoConfig. DATABASE_TWO_MONGO_TEMPLATE;

@Configuration
@EnableReactiveMongoRepositories(
        basePackages = {"com.demo.repository.database_two"},
        reactiveMongoTemplateRef = DATABASE_TWO_MONGO_TEMPLATE
)
public class DatabaseTwoMongoConfig {

}
```

_Step 6: Integration testing_

Lastly, add integration testing with Testcontainers to validate the behavior of your application.


## Summary

In this article, we explored the process of seamlessly integrating and managing multiple MongoDB databases within a single Spring Boot application using reactive programming. We began by configuring the application properties, specifying essential MongoDB connection details for each database.

Next, we created Spring Data MongoDB repositories for each MongoDB collection, ensuring an organized project structure that enhances code readability.

To simplify configuration and improve maintainability, we introduced a configuration properties class to store database settings per prefix. This step not only streamlined the code but also allowed for additional validations.

Our main configuration class, BaseMongoConfig, defined two MongoDB templates—one for each database—using the ReactiveMongoTemplate. This setup enabled us to interact with MongoDB collections in a reactive and non-blocking manner.

To inform Spring Boot about the repositories associated with each MongoDB template, we created separate configuration classes for each database. These classes specified the base package and referenced the correct MongoDB template, ensuring that Spring Boot could scan the repositories accurately.

Lastly, we highlighted the importance of integration testing with Testcontainers to verify the behavior of our application, offering readers a practical way to validate their implementations.

By following this comprehensive guide, developers can effectively manage multiple MongoDB databases within their Spring Boot applications while harnessing the power of reactive programming for high concurrency and responsiveness. This approach is invaluable in scenarios where diverse data requirements meet the demands of modern, data-intensive applications.

## Resources

1. [Spring Data MongoDB - Reference Documentation](https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/)
2. [Spring Data Reactive Repositories with MongoDB](https://www.baeldung.com/spring-data-mongodb-reactive)
3. [EnableReactiveMongoRepositories](https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/config/EnableReactiveMongoRepositories.html)


* * *
Finding my articles helpful? You could give me a caffeine boost to keep them coming! Your [coffee donation](https://www.paypal.com/donate/?hosted_button_id=M3CP3GHUVHEXQ) will keep my keyboard clacking and my ideas brewing. But remember, it's completely optional. Stay tuned, stay informed, and perhaps, [keep the coffee flowing](https://www.paypal.com/donate/?hosted_button_id=M3CP3GHUVHEXQ)!
![keep the coffee flowing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyl3stjvhahszobv89ta.png)