diff --git a/spring-reactive-modules/pom.xml b/spring-reactive-modules/pom.xml
index c349707027..6ab85c88b4 100644
--- a/spring-reactive-modules/pom.xml
+++ b/spring-reactive-modules/pom.xml
@@ -30,6 +30,9 @@
spring-reactive-exceptions
spring-reactor
spring-webflux-amqp
+
+
+
diff --git a/spring-reactive-modules/spring-reactive-performance/.gitignore b/spring-reactive-modules/spring-reactive-performance/.gitignore
new file mode 100644
index 0000000000..82eca336e3
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/.gitignore
@@ -0,0 +1,25 @@
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/README.md b/spring-reactive-modules/spring-reactive-performance/README.md
new file mode 100644
index 0000000000..16aac3d419
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/README.md
@@ -0,0 +1,3 @@
+## Spring Reactive Performance
+
+This module contains articles about reactive Spring Boot.
diff --git a/spring-reactive-modules/spring-reactive-performance/pom.xml b/spring-reactive-modules/spring-reactive-performance/pom.xml
new file mode 100644
index 0000000000..d7a69560dd
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/pom.xml
@@ -0,0 +1,77 @@
+
+
+ 4.0.0
+ com.baeldung.spring
+ spring-reactive-performance
+ 1.0.0-SNAPSHOT
+ spring-reactive-performance
+ jar
+ Spring Reactive Performance
+
+
+ com.baeldung.spring.reactive
+ spring-reactive-modules
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 21
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ --enable-preview
+
+
+
+
+
+
+ 21
+ 3.2.0
+
+
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/Application.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/Application.java
new file mode 100644
index 0000000000..284ddfce30
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/Application.java
@@ -0,0 +1,13 @@
+package com.baeldung.spring.reactive.performance;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/KafkaTemplate.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/KafkaTemplate.java
new file mode 100644
index 0000000000..cf8684690e
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/KafkaTemplate.java
@@ -0,0 +1,14 @@
+package com.baeldung.spring.reactive.performance;
+
+import java.util.concurrent.CompletableFuture;
+
+public class KafkaTemplate {
+
+ // For simplicity in this example and article, an actual Kafka client isn't utilized.
+ // The focus remains on demonstrating the basic principles without the complexities of a full Kafka client setup.
+
+ public CompletableFuture send(String topic, K key, V value) {
+ System.out.println("Sending message to topic: " + topic + " with value: " + value);
+ return CompletableFuture.completedFuture(null);
+ }
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/ProductAddedToCartEvent.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/ProductAddedToCartEvent.java
new file mode 100644
index 0000000000..08743d109c
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/ProductAddedToCartEvent.java
@@ -0,0 +1,6 @@
+package com.baeldung.spring.reactive.performance;
+
+import java.math.BigDecimal;
+
+public record ProductAddedToCartEvent(String productId, BigDecimal price, String currency, String cartId) {
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Price.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Price.java
new file mode 100644
index 0000000000..271ca26c96
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Price.java
@@ -0,0 +1,9 @@
+package com.baeldung.spring.reactive.performance.model;
+
+import java.math.BigDecimal;
+
+public record Price(BigDecimal value, String currency) {
+ public Price applyDiscount(BigDecimal discount) {
+ return new Price(value.subtract(discount), currency);
+ }
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Product.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Product.java
new file mode 100644
index 0000000000..d4aa751089
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Product.java
@@ -0,0 +1,43 @@
+package com.baeldung.spring.reactive.performance.model;
+
+import java.math.BigDecimal;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+@Document(collation = "products")
+public record Product (
+ @Id
+ String id,
+ String name,
+ BigDecimal basePriceValue,
+ String currency,
+ Category category
+) {
+
+ public Price basePrice() {
+ return new Price(basePriceValue, currency);
+ }
+
+ public enum Category {
+ ELECTRONICS(false),
+ CLOTHING(true),
+ ACCESSORIES(false),
+ GARDENING(false),
+ SPORTS(true);
+
+ private final boolean eligibleForPromotion;
+
+ Category(boolean eligibleForPromotion) {
+ this.eligibleForPromotion = eligibleForPromotion;
+ }
+
+ public boolean isEligibleForDiscount() {
+ return eligibleForPromotion;
+ }
+
+ public boolean notEligibleForPromotion() {
+ return !eligibleForPromotion;
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/DiscountService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/DiscountService.java
new file mode 100644
index 0000000000..89554396b2
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/DiscountService.java
@@ -0,0 +1,11 @@
+package com.baeldung.spring.reactive.performance.virtualthreads;
+
+import java.math.BigDecimal;
+
+class DiscountService {
+
+ public BigDecimal discountForProduct(String productId) {
+ return BigDecimal.valueOf(10);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductRepository.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductRepository.java
new file mode 100644
index 0000000000..f0ebff5cfd
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductRepository.java
@@ -0,0 +1,8 @@
+package com.baeldung.spring.reactive.performance.virtualthreads;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+
+import com.baeldung.spring.reactive.performance.model.Product;
+
+interface ProductRepository extends MongoRepository {
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductService.java
new file mode 100644
index 0000000000..f174e4c6fe
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductService.java
@@ -0,0 +1,45 @@
+package com.baeldung.spring.reactive.performance.virtualthreads;
+
+import java.math.BigDecimal;
+
+import com.baeldung.spring.reactive.performance.KafkaTemplate;
+import com.baeldung.spring.reactive.performance.ProductAddedToCartEvent;
+import com.baeldung.spring.reactive.performance.model.Price;
+import com.baeldung.spring.reactive.performance.model.Product;
+
+class ProductService {
+ private final String PRODUCT_ADDED_TO_CART_TOPIC = "product-added-to-cart";
+
+ private final ProductRepository repository;
+ private final DiscountService discountService;
+ private final KafkaTemplate kafkaTemplate;
+
+ public ProductService(ProductRepository repository, DiscountService discountService) {
+ this.repository = repository;
+ this.discountService = discountService;
+ this.kafkaTemplate = new KafkaTemplate<>();
+ }
+
+ public void addProductToCart(String productId, String cartId) {
+ Thread.startVirtualThread(() -> computePriceAndPublishMessage(productId, cartId));
+ }
+
+ private void computePriceAndPublishMessage(String productId, String cartId) {
+ Product product = repository.findById(productId)
+ .orElseThrow(() -> new IllegalArgumentException("not found!"));
+
+ Price price = computePrice(productId, product);
+
+ var event = new ProductAddedToCartEvent(productId, price.value(), price.currency(), cartId);
+ kafkaTemplate.send(PRODUCT_ADDED_TO_CART_TOPIC, cartId, event);
+ }
+
+ private Price computePrice(String productId, Product product) {
+ if (product.category().isEligibleForDiscount()) {
+ BigDecimal discount = discountService.discountForProduct(productId);
+ return product.basePrice().applyDiscount(discount);
+ }
+ return product.basePrice();
+ }
+
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/DiscountService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/DiscountService.java
new file mode 100644
index 0000000000..ebe6b9a8a4
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/DiscountService.java
@@ -0,0 +1,11 @@
+package com.baeldung.spring.reactive.performance.webflux;
+
+import java.math.BigDecimal;
+
+import reactor.core.publisher.Mono;
+
+class DiscountService {
+ public Mono discountForProduct(String productId) {
+ return Mono.just(BigDecimal.valueOf(10));
+ }
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductRepository.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductRepository.java
new file mode 100644
index 0000000000..5ab4497a27
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductRepository.java
@@ -0,0 +1,8 @@
+package com.baeldung.spring.reactive.performance.webflux;
+
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+
+import com.baeldung.spring.reactive.performance.model.Product;
+
+interface ProductRepository extends ReactiveMongoRepository {
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductService.java
new file mode 100644
index 0000000000..90519ec627
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductService.java
@@ -0,0 +1,39 @@
+package com.baeldung.spring.reactive.performance.webflux;
+
+import com.baeldung.spring.reactive.performance.KafkaTemplate;
+import com.baeldung.spring.reactive.performance.ProductAddedToCartEvent;
+import com.baeldung.spring.reactive.performance.model.Price;
+import com.baeldung.spring.reactive.performance.model.Product;
+
+import reactor.core.publisher.Mono;
+
+class ProductService {
+ private final String PRODUCT_ADDED_TO_CART_TOPIC = "product-added-to-cart";
+
+ private final ProductRepository repository;
+ private final DiscountService discountService;
+ private final KafkaTemplate kafkaTemplate;
+
+ public ProductService(ProductRepository repository, DiscountService discountService) {
+ this.repository = repository;
+ this.discountService = discountService;
+ this.kafkaTemplate = new KafkaTemplate<>();
+ }
+
+ public void addProductToCart(String productId, String cartId) {
+ repository.findById(productId)
+ .switchIfEmpty(Mono.error(() -> new IllegalArgumentException("not found!")))
+ .flatMap(this::computePrice)
+ .map(price -> new ProductAddedToCartEvent(productId, price.value(), price.currency(), cartId))
+ .subscribe(event -> kafkaTemplate.send(PRODUCT_ADDED_TO_CART_TOPIC, cartId, event));
+ }
+
+ private Mono computePrice(Product product) {
+ if (product.category().isEligibleForDiscount()) {
+ return discountService.discountForProduct(product.id())
+ .map(product.basePrice()::applyDiscount);
+ }
+ return Mono.just(product.basePrice());
+ }
+
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/resources/logback.xml b/spring-reactive-modules/spring-reactive-performance/src/main/resources/logback.xml
new file mode 100644
index 0000000000..7d900d8ea8
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-performance/src/test/java/com/baeldung/spring/reactive/performance/ApplicationUnitTest.java b/spring-reactive-modules/spring-reactive-performance/src/test/java/com/baeldung/spring/reactive/performance/ApplicationUnitTest.java
new file mode 100644
index 0000000000..df69e7eb72
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-performance/src/test/java/com/baeldung/spring/reactive/performance/ApplicationUnitTest.java
@@ -0,0 +1,12 @@
+package com.baeldung.spring.reactive.performance;
+
+import org.junit.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+public class ApplicationUnitTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+}