DEV Community

Pavel Zeger
Pavel Zeger

Posted on • Updated on

Reactive MongoDB with multi-database within a single Spring Boot application

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.



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


Enter fullscreen mode Exit fullscreen mode

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.



package com.demo.repository.database_one;

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

public interface CollectionOneRepository extends ReactiveMongoRepository<CollectionOne, String> {

}


Enter fullscreen mode Exit fullscreen mode


package com.demo.repository.database_two;

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

public interface CollectionTwoRepository extends ReactiveMongoRepository<CollectionTwo, String> {

}


Enter fullscreen mode Exit fullscreen mode

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.



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;

}


Enter fullscreen mode Exit fullscreen mode

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.



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();
    }

}


Enter fullscreen mode Exit fullscreen mode

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.



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 {

}


Enter fullscreen mode Exit fullscreen mode


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 {

}


Enter fullscreen mode Exit fullscreen mode

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
  2. Spring Data Reactive Repositories with MongoDB
  3. EnableReactiveMongoRepositories

Finding my articles helpful? You could give me a caffeine boost to keep them coming! Your coffee donation 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!
keep the coffee flowing

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.