Building a Reactive Microservice Architecture with Spring Cloud, WebFlux, and MongoDB
This tutorial demonstrates how to create a full‑reactive microservice system using Spring Cloud Finchley, WebFlux, Spring Data Reactive MongoDB, and Eureka for service discovery, covering project setup, service registration, reactive repositories, controllers, inter‑service calls with WebClient, and load‑balanced testing.
Spring Cloud traditionally uses synchronous REST calls via RestTemplate or Feign, but with Spring Cloud Finchley we can build a completely reactive stack using WebFlux and Spring Data Reactive.
Prerequisites : a single‑node Eureka server, two microservices (account and customer), and a MongoDB instance (run via Docker).
Start Eureka Server :
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency> spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8000/eureka/
server:
port: 8000 @EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}Account Service (cloud‑account) :
docker run -d --name mongo -p 27017:27017 mongo <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency> spring:
application:
name: cloud-account
server:
port: 8100
eureka:
client:
service-url:
defaultZone: http://localhost:8000/eureka/ @AllArgsConstructor
@NoArgsConstructor
@Data
@Document(collection = "accounts")
public class Account {
@Id
private String id;
private String customerId;
private Double amount;
} public interface AccountMongoReactiveRepository extends ReactiveCrudRepository
{
Flux
findByCustomerId(String customerId);
} @RequestMapping("/account")
@RestController
public class AccountController {
@Autowired
private AccountMongoReactiveRepository repository;
@GetMapping("/customer/{customer}")
public Flux
findByCustomer(@PathVariable("customer") String customer) {
System.out.println("Customer => " + customer + " [ " +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")) + " ]");
return repository.findByCustomerId(customer);
}
}Customer Service (cloud‑customer) uses the same dependencies and a similar configuration, only changing the application name and port (8200).
spring:
application:
name: cloud-customer
server:
port: 8200
eureka:
client:
service-url:
defaultZone: http://localhost:8000/eureka/ @Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "customers")
public class Customer {
@Id
private String id;
private String name;
private String mobile;
} public interface CustomerMongoReactiveRepository extends ReactiveCrudRepository
{} @RestController
@RequestMapping("/customer")
public class CustomerController {
@Autowired
private CustomerMongoReactiveRepository repository;
@Autowired
private WebClient.Builder webClientBuilder;
@GetMapping("")
public Flux
list() {
return repository.findAll();
}
@GetMapping("/{id}")
public Mono
get(@PathVariable String id) {
return repository.findById(id);
}
@PostMapping("")
public Mono
create(@RequestBody Customer customer) {
return repository.save(customer);
}
@PutMapping("/{id}")
public Mono
update(@PathVariable("id") String id, @RequestBody Customer customer) {
customer.setId(id);
return repository.save(customer);
}
@DeleteMapping("/{id}")
public Mono
delete(@PathVariable String id) {
return repository.deleteById(id);
}
@GetMapping("/{id}/account")
public Flux
getAllAccounts(@PathVariable String id) {
return webClientBuilder.baseUrl("http://cloud-account/account/")
.build()
.get()
.uri("/customer/" + id)
.retrieve()
.bodyToFlux(Account.class);
}
}WebClient Configuration (load‑balanced):
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}Running two instances of cloud‑account (ports 8100 and 8101) and invoking http://localhost:8200/customer/{id}/account shows alternating timestamps, confirming that WebClient together with Ribbon provides client‑side load balancing.
Conclusion : The example covers service registration, reactive data access, and non‑blocking inter‑service calls, achieving a full reactive stack backend. It illustrates the practical steps needed to move from traditional synchronous Spring Cloud services to a fully reactive, cloud‑native architecture.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.