Spring MongoDB Functional Reactive Microservices Example

Introduction

In this three pages post we will see how to create Spring MongoDB Functional Reactive Microservices Example. We are going to use here Spring 5’s Webflux API. We will also see here how to use Flux and Mono with ServerResponse, ServerRequest in Handler function. We will know how to use RequestPredicates in Router function in subsequent sections. We will use Spring Cloud, Eureka server and Ribbon for implementing our example.

If you want to know about reactive or functional reactive programming then you need to go through Spring Boot Functional Reactive Programming. You can also check my previous tutorial on Microservices using Spring Boot and Gradle. You may also like to read Spring Boot MongoDB Reactive Example.

Prerequisites

Eclipse Neon, Java 1.8, Gradle 5.4.1, Spring Boot 2.1.6, Spring Cloud, MongoDB 4

Installing Zip version of mongoDB in Windows

Implementation of Example

We will create the similar microservices as we have in our previous tutorial on Microservices using Spring Boot and Gradle but in a functional reactive programming way with MongoDB.

Creating Microservices

Microservice – Forex

Creating Project

Create a gradle based project in Eclipse, the project name is spring-boot-reactive-forex-microservice.

Updating Build Script

Update the default generated build.gradle script as shown below.

Notice we have used Reactive Mongodb and Spring Webflux API.

buildscript {
    ext {
        springBootVersion = '2.1.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
plugins {
    id "io.spring.dependency-management" version "1.0.8.RELEASE"
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
    mavenCentral()
}
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux:${springBootVersion}")
    implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive:${springBootVersion}")
}
dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.RELEASE'
    }
}
Configuring MongoDB

As we mentioned in the prerequisites section, you need to go through Installing MongoDB in Windowsto install MongoDB and create database roytuts into it.

If we do not specify anything in the application.properties or application.yml file then Spring Boot, by default, connects to test database server at localhost:27017.

Here we will specify our roytuts database for connecting to it.

Notice also we have mentioned spring.jackson.default-property-inclusion=NON_NULL to ignore null field in JSON response.

We have also provided our microservice application’s name.

Create below content into src/main/resources/application.properties file.

spring.data.mongodb.database=roytuts
spring.application.name=forex-microservice
spring.jackson.default-property-inclusion=NON_NULL
Creating Entity Class

We are creating the below entity class to persist data into MongoDB. We have specified collection name using @Document annotation.

Though the collection name here is same as the entity class but if you have different collection name from your entity name then you can specify collection name on entity class. If your entity class and collection name is same then you don’t need to specify the collection name.

We have annotated id attribute of a particular row in a collection using @Id annotation.

package com.roytuts.spring.boot.reactive.forex.microservice.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "forex")
public class Forex {
	@Id
	private String id;
	private String fromCur;
	private String toCur;
	private Double rateCur;
	public Forex() {
	}
	public Forex(String id, String fromCur, String toCur, Double rateCur) {
		this.id = id;
		this.fromCur = fromCur;
		this.toCur = toCur;
		this.rateCur = rateCur;
	}
	//getters and setters
}
Creating Reactive Repository

We will create a repository interface that extends ReactiveCrudRepository because we are creating Reactive application.

We have included one method in this repository for fetching currency conversion value from one currency to another currency.

See how we are fetching single entity using Webflux’s Mono API.

package com.roytuts.spring.boot.reactive.forex.microservice.repository;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import com.roytuts.spring.boot.reactive.forex.microservice.entity.Forex;
import reactor.core.publisher.Mono;
public interface ForexRepository extends ReactiveCrudRepository<Forex, String> {
	Mono<Forex> findByFromCurAndToCur(String fromCur, String toCur);
}
Creating VO Class

It’s always good idea to create DTO or VO class to send as a response object from business layer.

We have the below VO class corresponds to the entity class.

package com.roytuts.spring.boot.reactive.forex.microservice.vo;
public class ForexVo {
	private String id;
	private String fromCur;
	private String toCur;
	private Double rateCur;
	public ForexVo() {
	}
	public ForexVo(String id, String fromCur, String toCur, Double rateCur) {
		this.id = id;
		this.fromCur = fromCur;
		this.toCur = toCur;
		this.rateCur = rateCur;
	}
	//getters and setters
}
Creating Handler Functions

Handler function is one of the two important functions of Reactive Streams – Handler Function and Router Function.

To know more on Handler Function you may read here and here.

Notice in the below class how we are converting Mono entity object to Mono VO object.

package com.roytuts.spring.boot.reactive.forex.microservice.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.roytuts.spring.boot.reactive.forex.microservice.entity.Forex;
import com.roytuts.spring.boot.reactive.forex.microservice.repository.ForexRepository;
import com.roytuts.spring.boot.reactive.forex.microservice.vo.ForexVo;
import reactor.core.publisher.Mono;
@Component
public class ForexHandler {
	@Autowired
	private ForexRepository repository;
	public Mono<ServerResponse> getForexByFromCurToCur(ServerRequest request) {
		String fromCur = request.pathVariable("fromCur");
		String toCur = request.pathVariable("toCur");
		Mono<ServerResponse> notFound = ServerResponse.notFound().build();
		Mono<Forex> forex = repository.findByFromCurAndToCur(fromCur, toCur);
		Mono<ForexVo> forexVo = Mono.from(forex.map(f -> {
			return new ForexVo(f.getId(), f.getFromCur(), f.getToCur(), f.getRateCur());
		}));
		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(forexVo, ForexVo.class)
				.switchIfEmpty(notFound);
	}
}
Creating Router Functions

This is another important function of reactive stream as previously mentioned while writing Router Function.

To know more on Router Function you may read here and here.

package com.roytuts.spring.boot.reactive.forex.microservice.router;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.roytuts.spring.boot.reactive.forex.microservice.handler.ForexHandler;
@Configuration
public class ForexRouter {
	@Bean
	public RouterFunction<ServerResponse> route(ForexHandler handler) {
		return RouterFunctions.route(RequestPredicates.GET("/forex/fromCur/{fromCur}/toCur/{toCur}")
				.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::getForexByFromCurToCur);
	}
}
Creating Main Class

Creating a main would be sufficient to deploy our application into the Tomcat server.

This is a great advantage that you just need to let Spring know that it is your Spring Boot Application using @SpringBootApplication and main class.

We also need to tell Spring where our entity class and reactive repository interface.

package com.roytuts.spring.boot.reactive.forex.microservice.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
@EntityScan("com.roytuts.spring.boot.reactive.forex.microservice.entity")
@EnableReactiveMongoRepositories("com.roytuts.spring.boot.reactive.forex.microservice.repository")
@SpringBootApplication(scanBasePackages = "com.roytuts.spring.boot.reactive.forex.microservice")
public class ReactiveForexMicroSvcApp {
	public static void main(String[] args) {
		SpringApplication.run(ReactiveForexMicroSvcApp.class, args);
	}
}
Inserting Data into MongoDB

We need to insert some data to test our application. You can use my previous example to insert data or you can use MongoDB command line tool to insert data.

Here I am going to insert data using MongoDB client tool using below command.

Make sure your collection name forex exists in the MongoDB under roytuts database. You can have a look at the tutorial how to create database and collection in MongoDB.

db.forex.insert({"fromCur":"USD", "toCur":"INR", "rateCur":70 });
db.forex.insert({"fromCur" : "EUR", "toCur" : "INR", "rateCur" : 80});
Running Forex Microservice

Run the main class to deploy our microservice into the server. Your server will start on port 8080.

Testing the Forex Microservice

Now call the REST service using any REST client or on browser:

USD to INR Conversion

Request Method – GET

Request URL – http://localhost:8080/forex/fromCur/USD/toCur/INR

Response

{"id":"5d24b085f352a76d529c0b8f","fromCur":"USD","toCur":"INR","rateCur":70.0}
EUR to INR Conversion

Request Method – GET

Request URL – http://localhost:8080/forex/fromCur/EUR/toCur/INR

Response

{"id":"5d25cc66a17f66da6a7ab88e","fromCur":"EUR","toCur":"INR","rateCur":80.0}

Leave a Reply

Your email address will not be published. Required fields are marked *