diff --git a/spring-boot-modules/spring-boot-libraries-3/drawio/eventuate-tram.drawio b/spring-boot-modules/spring-boot-libraries-3/drawio/eventuate-tram.drawio
new file mode 100644
index 000000000000..b9daad2b6731
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/drawio/eventuate-tram.drawio
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-boot-modules/spring-boot-libraries-3/pom.xml b/spring-boot-modules/spring-boot-libraries-3/pom.xml
index ebb3dbbf41b3..c1d470aa82e4 100644
--- a/spring-boot-modules/spring-boot-libraries-3/pom.xml
+++ b/spring-boot-modules/spring-boot-libraries-3/pom.xml
@@ -16,6 +16,11 @@
org.springframework.boot
spring-boot-starter-data-jpa
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
org.springframework.kafka
spring-kafka
@@ -27,6 +32,17 @@
${postgresql.version}
+
+ io.eventuate.tram.core
+ eventuate-tram-spring-jdbc-kafka
+ ${eventuate.tram.version}
+
+
+ io.eventuate.tram.core
+ eventuate-tram-spring-events
+ ${eventuate.tram.version}
+
+
org.springframework.modulith
spring-modulith-events-api
@@ -84,6 +100,12 @@
test
+
+ net.javacrumbs.json-unit
+ json-unit-assertj
+ ${json-unit-assertj.version}
+ test
+
org.awaitility
awaitility
@@ -112,7 +134,23 @@
42.3.1
2.2.224
2.2.1
+ 0.36.0.RELEASE
+ 3.5.0
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 21
+ 21
+
+
+
+
+
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/Application.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/Application.java
similarity index 77%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/Application.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/Application.java
index aeaf57becd4d..4acf4847b716 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/Application.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/Application.java
@@ -1,8 +1,11 @@
-package com.baeldung.springmodulith;
+package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+/**
+ * use the appropriate profile: eventuate|modulith
+ */
@SpringBootApplication
public class Application {
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/CommentsController.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/CommentsController.java
new file mode 100644
index 000000000000..5fb010fe30c4
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/CommentsController.java
@@ -0,0 +1,32 @@
+package com.baeldung.eventuate.tram;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.baeldung.eventuate.tram.domain.CommentService;
+import com.baeldung.eventuate.tram.domain.Comment;
+
+@RestController
+public class CommentsController {
+
+ private final CommentService commentService;
+
+ public CommentsController(CommentService service) {
+ this.commentService = service;
+ }
+
+ @PostMapping("/api/articles/{slug}/comments")
+ public ResponseEntity addComment(@RequestBody AddCommentDto dto, @PathVariable String slug) {
+ Comment comment = new Comment(dto.text(), slug, dto.commentAuthor());
+ long id = commentService.save(comment);
+ return ResponseEntity.status(HttpStatus.CREATED)
+ .body(id);
+ }
+
+ record AddCommentDto(String text, String commentAuthor) {
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/EventuateConfig.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/EventuateConfig.java
new file mode 100644
index 000000000000..653d7b3c2841
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/EventuateConfig.java
@@ -0,0 +1,13 @@
+package com.baeldung.eventuate.tram;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration;
+import io.eventuate.tram.spring.messaging.producer.jdbc.TramMessageProducerJdbcConfiguration;
+
+@Configuration
+@Import({ TramEventsPublisherConfiguration.class, TramMessageProducerJdbcConfiguration.class })
+class EventuateConfig {
+
+}
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/Comment.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/Comment.java
new file mode 100644
index 000000000000..651f621ce417
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/Comment.java
@@ -0,0 +1,42 @@
+package com.baeldung.eventuate.tram.domain;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+@Entity
+public class Comment {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String text;
+ private String articleSlug;
+ private String commentAuthor;
+
+ public Comment(String text, String articleSlug, String commentAuthor) {
+ this.text = text;
+ this.articleSlug = articleSlug;
+ this.commentAuthor = commentAuthor;
+ }
+
+ Comment() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public String getArticleSlug() {
+ return articleSlug;
+ }
+
+ public String getCommentAuthor() {
+ return commentAuthor;
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentAddedEvent.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentAddedEvent.java
new file mode 100644
index 000000000000..dd30cfb9fba9
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentAddedEvent.java
@@ -0,0 +1,6 @@
+package com.baeldung.eventuate.tram.domain;
+
+import io.eventuate.tram.events.common.DomainEvent;
+
+record CommentAddedEvent(Long id, String articleSlug) implements DomainEvent {
+}
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentRepository.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentRepository.java
new file mode 100644
index 000000000000..b9fa666536ce
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentRepository.java
@@ -0,0 +1,6 @@
+package com.baeldung.eventuate.tram.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CommentRepository extends JpaRepository {
+}
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentService.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentService.java
new file mode 100644
index 000000000000..e4d2e291c5b5
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/eventuate/tram/domain/CommentService.java
@@ -0,0 +1,35 @@
+package com.baeldung.eventuate.tram.domain;
+
+import static java.util.Collections.singletonList;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import io.eventuate.tram.events.publisher.DomainEventPublisher;
+import jakarta.transaction.Transactional;
+
+@Service
+public class CommentService {
+
+ private static final Logger log = LoggerFactory.getLogger(CommentService.class);
+
+ private final CommentRepository comments;
+ private final DomainEventPublisher domainEvents;
+
+ public CommentService(CommentRepository commentRepository, DomainEventPublisher domainEvents) {
+ this.comments = commentRepository;
+ this.domainEvents = domainEvents;
+ }
+
+ @Transactional
+ public Long save(Comment comment) {
+ Comment saved = this.comments.save(comment);
+ log.info("Comment created: {}", saved);
+
+ CommentAddedEvent commentAdded = new CommentAddedEvent(saved.getId(), saved.getArticleSlug());
+ domainEvents.publish("baeldung.comment.added", saved.getId(), singletonList(commentAdded));
+ return saved.getId();
+ }
+
+}
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/Order.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/Order.java
similarity index 81%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/Order.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/Order.java
index c448bd44ddde..1acc8b4d5f7e 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/Order.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/Order.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events.orders;
+package com.baeldung.spring.modulith.events.orders;
import java.time.Instant;
import java.util.List;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderCompletedEvent.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderCompletedEvent.java
similarity index 65%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderCompletedEvent.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderCompletedEvent.java
index 4344b336ac1b..1da9231ca22e 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderCompletedEvent.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderCompletedEvent.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events.orders;
+package com.baeldung.spring.modulith.events.orders;
import java.time.Instant;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderRepository.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderRepository.java
similarity index 91%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderRepository.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderRepository.java
index 7c159e358206..e485c004019b 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderRepository.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderRepository.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events.orders;
+package com.baeldung.spring.modulith.events.orders;
import java.util.ArrayList;
import java.util.List;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderService.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderService.java
similarity index 93%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderService.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderService.java
index c60792813c1a..536d0623d7d4 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/orders/OrderService.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/orders/OrderService.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events.orders;
+package com.baeldung.spring.modulith.events.orders;
import java.util.Arrays;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyalCustomersRepository.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/rewards/LoyalCustomersRepository.java
similarity index 94%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyalCustomersRepository.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/rewards/LoyalCustomersRepository.java
index 29ba6fa8e271..24e1c888fe48 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyalCustomersRepository.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/rewards/LoyalCustomersRepository.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events.rewards;
+package com.baeldung.spring.modulith.events.rewards;
import java.util.ArrayList;
import java.util.List;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyaltyPointsService.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/rewards/LoyaltyPointsService.java
similarity index 81%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyaltyPointsService.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/rewards/LoyaltyPointsService.java
index 8cd1afe329bc..2f4cd084c72d 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/application/events/rewards/LoyaltyPointsService.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/events/rewards/LoyaltyPointsService.java
@@ -1,9 +1,9 @@
-package com.baeldung.springmodulith.application.events.rewards;
+package com.baeldung.spring.modulith.events.rewards;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
-import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent;
+import com.baeldung.spring.modulith.events.orders.OrderCompletedEvent;
@Service
public class LoyaltyPointsService {
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/Article.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/Article.java
similarity index 93%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/Article.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/Article.java
index d52ed5afe5a8..fc03becdd11e 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/Article.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/Article.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
import static jakarta.persistence.GenerationType.*;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/ArticlePublishedEvent.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/ArticlePublishedEvent.java
similarity index 75%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/ArticlePublishedEvent.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/ArticlePublishedEvent.java
index e12b6dafe5ff..d189cc30fb85 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/ArticlePublishedEvent.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/ArticlePublishedEvent.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
import org.springframework.modulith.events.Externalized;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/ArticleRepository.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/ArticleRepository.java
similarity index 76%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/ArticleRepository.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/ArticleRepository.java
index f6351b6262b5..4a3ef1b699b9 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/ArticleRepository.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/ArticleRepository.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/Baeldung.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/Baeldung.java
similarity index 94%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/Baeldung.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/Baeldung.java
index 4b861a49c853..6aefaeee957b 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/Baeldung.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/Baeldung.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/EventExternalizationConfig.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/EventExternalizationConfig.java
similarity index 97%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/EventExternalizationConfig.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/EventExternalizationConfig.java
index 6555694df983..561b86dfa3f2 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/EventExternalizationConfig.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/EventExternalizationConfig.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/WeeklySummaryPublishedEvent.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/WeeklySummaryPublishedEvent.java
similarity index 70%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/WeeklySummaryPublishedEvent.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/WeeklySummaryPublishedEvent.java
index 2ae8713099c8..d3e2d01182e1 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/WeeklySummaryPublishedEvent.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/WeeklySummaryPublishedEvent.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
import org.springframework.modulith.events.Externalized;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/infra/ArticlePublishedKafkaProducer.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/infra/ArticlePublishedKafkaProducer.java
similarity index 89%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/infra/ArticlePublishedKafkaProducer.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/infra/ArticlePublishedKafkaProducer.java
index 17a88a73f706..9dc11aa75bf8 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/infra/ArticlePublishedKafkaProducer.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/infra/ArticlePublishedKafkaProducer.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization.infra;
+package com.baeldung.spring.modulith.externalization.infra;
import org.springframework.context.event.EventListener;
import org.springframework.kafka.core.KafkaOperations;
@@ -6,7 +6,7 @@
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.util.Assert;
-import com.baeldung.springmodulith.events.externalization.ArticlePublishedEvent;
+import com.baeldung.spring.modulith.externalization.ArticlePublishedEvent;
//@Component
// this is used in sections 3 and 4 of tha article
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/infra/PublicationEvents.java b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/infra/PublicationEvents.java
similarity index 89%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/infra/PublicationEvents.java
rename to spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/infra/PublicationEvents.java
index bf0b96e78bd5..936b4d0e11a2 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/springmodulith/events/externalization/infra/PublicationEvents.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/java/com/baeldung/spring/modulith/externalization/infra/PublicationEvents.java
@@ -1,6 +1,5 @@
-package com.baeldung.springmodulith.events.externalization.infra;
+package com.baeldung.spring.modulith.externalization.infra;
-import com.baeldung.springmodulith.events.externalization.ArticlePublishedEvent;
import org.springframework.modulith.events.CompletedEventPublications;
import org.springframework.modulith.events.IncompleteEventPublications;
import org.springframework.stereotype.Component;
@@ -8,6 +7,8 @@
import java.time.Duration;
import java.time.Instant;
+import com.baeldung.spring.modulith.externalization.ArticlePublishedEvent;
+
@Component
class PublicationEvents {
private final IncompleteEventPublications incompleteEvent;
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/resources/application-eventuate.yml b/spring-boot-modules/spring-boot-libraries-3/src/main/resources/application-eventuate.yml
new file mode 100644
index 000000000000..c4968a68e748
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/main/resources/application-eventuate.yml
@@ -0,0 +1,23 @@
+spring:
+ datasource:
+ url: jdbc:postgresql://localhost:5432/mydb
+ username: sa
+ password: password
+ driver-class-name: org.postgresql.Driver
+ jpa:
+ generate-ddl: true
+ properties:
+ hibernate:
+ hbm2ddl.auto: create
+ kafka:
+ bootstrap-servers: localhost:9092
+
+logging:
+ level:
+ root: INFO
+ io.eventuate: DEBUG
+
+# not needed for this article:
+spring.modulith:
+ republish-outstanding-events-on-restart: false
+ events.jdbc.schema-initialization.enabled: false
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/main/resources/application.yml b/spring-boot-modules/spring-boot-libraries-3/src/main/resources/application-modulith.yml
similarity index 100%
rename from spring-boot-modules/spring-boot-libraries-3/src/main/resources/application.yml
rename to spring-boot-modules/spring-boot-libraries-3/src/main/resources/application-modulith.yml
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/eventuate/tram/EventuateTramLiveTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/eventuate/tram/EventuateTramLiveTest.java
new file mode 100644
index 000000000000..e9e3ec8587ad
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/eventuate/tram/EventuateTramLiveTest.java
@@ -0,0 +1,82 @@
+package com.baeldung.eventuate.tram;
+
+import static java.time.Duration.ofSeconds;
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
+
+import java.io.File;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.servlet.MockMvc;
+import org.testcontainers.containers.ComposeContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+import com.baeldung.Application;
+import com.baeldung.listener.TestKafkaListenerConfig;
+import com.baeldung.listener.TestListener;
+
+@Testcontainers
+@AutoConfigureMockMvc
+@SpringBootTest(classes = { Application.class, TestKafkaListenerConfig.class })
+@ActiveProfiles({ "eventuate", "test-listeners"})
+class EventuateTramLiveTest {
+
+ @Container
+ static ComposeContainer environment = new ComposeContainer(
+ new File("src/test/resources/eventuate-docker-compose.yml"))
+ .withLocalCompose(true);
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private TestListener testListener;
+
+ @BeforeEach
+ void setUp() {
+ testListener.reset();
+ }
+
+ @Test
+ void whenSavingAnEntityToDB_thenPublishKafkaEvent() throws Exception {
+ String commentId = mockMvc.perform(post("/api/articles/oop-best-practices/comments")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "articleAuthor": "Andrey the Author",
+ "commentAuthor": "Richard the Reader",
+ "text": "Great article!"
+ }
+ """))
+ .andExpect(status().is(201))
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ await().atMost(ofSeconds(30))
+ .until(() -> testListener.getCommentAddedEvents().size() == 1);
+
+ String eventJson = testListener.getCommentAddedEvents().getFirst();
+ assertThatJson(eventJson)
+ .inPath("payload").asString()
+ .isEqualToIgnoringWhitespace("""
+ {
+ "id": %s,
+ "articleSlug": "oop-best-practices"
+ }
+ """.formatted(commentId));
+ }
+
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/listener/TestKafkaListenerConfig.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/listener/TestKafkaListenerConfig.java
similarity index 91%
rename from spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/listener/TestKafkaListenerConfig.java
rename to spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/listener/TestKafkaListenerConfig.java
index c2ee9b24a20d..6960d931aab2 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/listener/TestKafkaListenerConfig.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/listener/TestKafkaListenerConfig.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.events.externalization.listener;
+package com.baeldung.listener;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
@@ -28,4 +28,8 @@ ConsumerFactory consumerFactory(KafkaProperties kafkaProperties
return new DefaultKafkaConsumerFactory<>(kafkaProperties.buildConsumerProperties());
}
+ @Bean
+ TestListener testListener() {
+ return new TestListener();
+ }
}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/listener/TestListener.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/listener/TestListener.java
new file mode 100644
index 000000000000..14c5063903b7
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/listener/TestListener.java
@@ -0,0 +1,34 @@
+package com.baeldung.listener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.kafka.annotation.KafkaListener;
+
+public class TestListener {
+ private List articlePublishedEvents = new ArrayList<>();
+ private List commentAddedEvents = new ArrayList<>();
+
+ @KafkaListener(id = "test-id", topics = "baeldung.articles.published")
+ public void listen(String event) {
+ articlePublishedEvents.add(event);
+ }
+
+ @KafkaListener(id = "test-id-2", topics = "baeldung.comment.added")
+ public void commentAddedEvents(String event) {
+ commentAddedEvents.add(event);
+ }
+
+ public List getArticlePublishedEvents() {
+ return articlePublishedEvents;
+ }
+
+ public List getCommentAddedEvents() {
+ return commentAddedEvents;
+ }
+
+ public void reset() {
+ articlePublishedEvents = new ArrayList<>();
+ commentAddedEvents = new ArrayList<>();
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventListenerUnitTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/EventListenerUnitTest.java
similarity index 76%
rename from spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventListenerUnitTest.java
rename to spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/EventListenerUnitTest.java
index 676bc1173b39..795a126ddf2e 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventListenerUnitTest.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/EventListenerUnitTest.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events;
+package com.baeldung.spring.modulith.events;
import static org.assertj.core.api.Assertions.assertThat;
@@ -8,11 +8,13 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.test.context.ActiveProfiles;
-import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent;
-import com.baeldung.springmodulith.application.events.rewards.LoyalCustomersRepository;
+import com.baeldung.spring.modulith.events.orders.OrderCompletedEvent;
+import com.baeldung.spring.modulith.events.rewards.LoyalCustomersRepository;
@SpringBootTest
+@ActiveProfiles({ "modulith", "h2" })
class EventListenerUnitTest {
@Autowired
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventPublisherUnitTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/EventPublisherUnitTest.java
similarity index 82%
rename from spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventPublisherUnitTest.java
rename to spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/EventPublisherUnitTest.java
index f4bdeee90dfa..a2b199bb0dc2 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/EventPublisherUnitTest.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/EventPublisherUnitTest.java
@@ -1,15 +1,17 @@
-package com.baeldung.springmodulith.application.events;
+package com.baeldung.spring.modulith.events;
-import com.baeldung.springmodulith.application.events.orders.OrderService;
+import com.baeldung.spring.modulith.events.orders.OrderService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
+@ActiveProfiles("h2")
class EventPublisherUnitTest {
@Autowired
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/SpringModulithScenarioApiUnitTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/SpringModulithScenarioApiUnitTest.java
similarity index 74%
rename from spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/SpringModulithScenarioApiUnitTest.java
rename to spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/SpringModulithScenarioApiUnitTest.java
index f36a0c30e628..a8393c6a2523 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/SpringModulithScenarioApiUnitTest.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/SpringModulithScenarioApiUnitTest.java
@@ -1,21 +1,21 @@
-package com.baeldung.springmodulith.application.events;
+package com.baeldung.spring.modulith.events;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Instant;
-import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent;
-import com.baeldung.springmodulith.application.events.orders.OrderService;
-import com.baeldung.springmodulith.application.events.rewards.LoyalCustomersRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.modulith.test.ApplicationModuleTest;
-import org.springframework.modulith.test.ApplicationModuleTest.BootstrapMode;
import org.springframework.modulith.test.Scenario;
+import org.springframework.test.context.ActiveProfiles;
-import java.time.Duration;
-import java.time.Instant;
-
-import static java.time.Duration.ofMillis;
-import static org.assertj.core.api.Assertions.assertThat;
+import com.baeldung.spring.modulith.events.orders.OrderCompletedEvent;
+import com.baeldung.spring.modulith.events.orders.OrderService;
+import com.baeldung.spring.modulith.events.rewards.LoyalCustomersRepository;
@ApplicationModuleTest
+@ActiveProfiles({ "modulith", "h2" })
class SpringModulithScenarioApiUnitTest {
@Autowired
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/TestEventListener.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/TestEventListener.java
similarity index 78%
rename from spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/TestEventListener.java
rename to spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/TestEventListener.java
index 8973a993551a..278f43a2a7b6 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/application/events/TestEventListener.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/events/TestEventListener.java
@@ -1,4 +1,4 @@
-package com.baeldung.springmodulith.application.events;
+package com.baeldung.spring.modulith.events;
import java.util.ArrayList;
import java.util.List;
@@ -6,7 +6,7 @@
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
-import com.baeldung.springmodulith.application.events.orders.OrderCompletedEvent;
+import com.baeldung.spring.modulith.events.orders.OrderCompletedEvent;
@Component
class TestEventListener {
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/EventsExternalizationLiveTest.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/externalization/EventsExternalizationLiveTest.java
similarity index 88%
rename from spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/EventsExternalizationLiveTest.java
rename to spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/externalization/EventsExternalizationLiveTest.java
index a1b3dfe170ce..83770d796849 100644
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/EventsExternalizationLiveTest.java
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/spring/modulith/externalization/EventsExternalizationLiveTest.java
@@ -1,12 +1,14 @@
-package com.baeldung.springmodulith.events.externalization;
+package com.baeldung.spring.modulith.externalization;
+
+import com.baeldung.Application;
+import com.baeldung.listener.TestKafkaListenerConfig;
+import com.baeldung.listener.TestListener;
-import com.baeldung.springmodulith.Application;
-import com.baeldung.springmodulith.events.externalization.listener.TestKafkaListenerConfig;
-import com.baeldung.springmodulith.events.externalization.listener.TestListener;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.KafkaContainer;
@@ -23,6 +25,7 @@
@Testcontainers
@SpringBootTest(classes = { Application.class, TestKafkaListenerConfig.class })
+@ActiveProfiles({ "modulith", "test-listeners" })
class EventsExternalizationLiveTest {
@Autowired
@@ -65,7 +68,7 @@ void whenArticleIsSavedToDB_thenItIsAlsoPublishedToKafka() {
baeldung.createArticle(article);
await().untilAsserted(() ->
- assertThat(listener.getEvents())
+ assertThat(listener.getArticlePublishedEvents())
.hasSize(1)
.first().asString()
.contains("\"slug\":\"introduction-to-spring-boot\"")
@@ -84,7 +87,7 @@ void whenPublishingMessageFails_thenArticleIsStillSavedToDB() {
baeldung.createArticle(article);
- assertThat(listener.getEvents())
+ assertThat(listener.getArticlePublishedEvents())
.isEmpty();
assertThat(repository.findAll())
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/listener/TestListener.java b/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/listener/TestListener.java
deleted file mode 100644
index bf5a36f66fc9..000000000000
--- a/spring-boot-modules/spring-boot-libraries-3/src/test/java/com/baeldung/springmodulith/events/externalization/listener/TestListener.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.baeldung.springmodulith.events.externalization.listener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.kafka.annotation.KafkaListener;
-import org.springframework.stereotype.Component;
-
-@Component
-public class TestListener {
- private List events = new ArrayList<>();
-
- @KafkaListener(id = "test-id", topics = "baeldung.articles.published")
- public void listen(String event) {
- events.add(event);
- }
-
- public List getEvents() {
- return events;
- }
-
- public void reset() {
- events = new ArrayList<>();
- }
-}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/resources/application-h2.yml b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/application-h2.yml
new file mode 100644
index 000000000000..73df29d333d6
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/application-h2.yml
@@ -0,0 +1,6 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:testdb
+ username: sa
+ password: pass
+ driver-class-name: org.h2.Driver
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/resources/application-test-listeners.yml b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/application-test-listeners.yml
new file mode 100644
index 000000000000..3b79e795185d
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/application-test-listeners.yml
@@ -0,0 +1,7 @@
+spring.kafka:
+ bootstrap-servers: localhost:9092
+ consumer:
+ group-id: test-group-id
+ key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
+ value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
+ auto-offset-reset: earliest
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/resources/eventuate-docker-compose.yml b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/eventuate-docker-compose.yml
new file mode 100644
index 000000000000..319b82bf74d4
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/eventuate-docker-compose.yml
@@ -0,0 +1,56 @@
+version: '3.8'
+
+services:
+ zookeeper:
+ image: eventuateio/eventuate-zookeeper:0.20.0.RELEASE
+ ports:
+ - 2181:2181
+ environment:
+ ZOOKEEPER_CLIENT_PORT: 2181
+ KAFKA_HEAP_OPTS: -Xmx64m
+
+ kafka:
+ image: eventuateio/eventuate-kafka:0.20.0.RELEASE
+ ports:
+ - 9092:9092
+ - 29092:29092
+ depends_on:
+ - zookeeper
+ environment:
+ KAFKA_LISTENERS: LC://kafka:29092,LX://kafka:9092
+ KAFKA_ADVERTISED_LISTENERS: LC://kafka:29092,LX://${DOCKER_HOST_IP:-localhost}:9092
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LC:PLAINTEXT,LX:PLAINTEXT
+ KAFKA_INTER_BROKER_LISTENER_NAME: LC
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+ KAFKA_HEAP_OPTS: -Xmx192m
+
+ postgres:
+ image: eventuateio/eventuate-postgres:0.20.0.RELEASE
+ restart: always
+ environment:
+ POSTGRES_USER: sa
+ POSTGRES_PASSWORD: password
+ POSTGRES_DB: mydb
+ ports:
+ - "5432:5432"
+
+ cdcservice:
+ image: eventuateio/eventuate-cdc-service:0.18.0.RELEASE
+ ports:
+ - "8099:8080"
+ depends_on:
+ - postgres
+ - kafka
+ - zookeeper
+ environment:
+ SPRING_PROFILES_ACTIVE: EventuatePolling
+ SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/mydb
+ SPRING_DATASOURCE_USERNAME: sa
+ SPRING_DATASOURCE_PASSWORD: password
+ SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.postgresql.Driver
+ EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092
+ EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181
+ EVENTUATELOCAL_CDC_READER_NAME: PostgresWalReader
+ EVENTUATE_OUTBOX_ID: 1
+
diff --git a/spring-boot-modules/spring-boot-libraries-3/src/test/resources/post-comment.bat b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/post-comment.bat
new file mode 100644
index 000000000000..17399e3ab3bb
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-3/src/test/resources/post-comment.bat
@@ -0,0 +1,3 @@
+curl --location "http://localhost:8080/api/articles/oop-best-practices/comments" ^
+--header "Content-Type: application/json" ^
+--data "{\"articleAuthor\": \"Andrey the Author\", \"text\": \"Great article!\", \"commentAuthor\": \"Richard the Reader\"}"
\ No newline at end of file