Lab 10: Use Spring Cloud Stream to Implement Event-Driven Communication Between Microservices (Spring Boot 3.4.1)
Learn how to use Spring Cloud Stream with Kafka as the message broker to enable event-driven communication among microservices. You’ll create a producer (publishing OrderEvent
s) and a consumer (receiving those events) in Spring Boot 3.4.1.
-
Install Java (required for Kafka).
- Verify Java by running:
java -version
- If missing, install JDK 17 (e.g., from AdoptOpenJDK).
- Verify Java by running:
-
Download Kafka.
- Go to Kafka Downloads and get the latest stable release (e.g.,
kafka_2.13-3.4.0.tgz
).
- Go to Kafka Downloads and get the latest stable release (e.g.,
-
Extract Kafka.
- For example:
tar -xvzf kafka_2.13-3.4.0.tgz -C /opt/kafka
- For example:
-
Start Zookeeper.
- In one terminal:
cd /opt/kafka ./bin/zookeeper-server-start.sh config/zookeeper.properties
- In one terminal:
-
Start the Kafka broker.
- In another terminal:
cd /opt/kafka ./bin/kafka-server-start.sh config/server.properties
- In another terminal:
-
Create a Kafka topic.
- For example, to create
order-events
:./bin/kafka-topics.sh --create --topic order-events --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1
- For example, to create
-
Verify the topic creation.
- List Kafka topics:
./bin/kafka-topics.sh --list --bootstrap-server localhost:9092
- Confirm
order-events
is present.
- List Kafka topics:
-
Generate a new Spring Boot project for
OrderService
.- Visit https://start.spring.io/.
- Spring Boot Version: 3.4.1
- Group Id:
com.microservices
- Artifact Id:
order-service
- Dependencies:
- Spring Web
- Spring Cloud Stream
- Spring Cloud Stream Kafka Binder
- Extract to
OrderService
.
-
Import
OrderService
into your IDE. -
Configure Spring Cloud Stream for Kafka.
- In
src/main/resources/application.properties
:spring.application.name=order-service spring.cloud.stream.kafka.binder.brokers=localhost:9092 spring.cloud.stream.bindings.output.destination=order-events
- This means the binding name is
output
, publishing to the topicorder-events
.
- In
-
Create an event model.
- In
OrderEvent.java
:package com.microservices.orderservice; public class OrderEvent { private String orderId; private String status; public OrderEvent(String orderId, String status) { this.orderId = orderId; this.status = status; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } @Override public String toString() { return "OrderEvent{" + "orderId='" + orderId + '\'' + ", status='" + status + '\'' + '}'; } }
- In
-
Create a message producer.
- In
OrderProducer.java
:package com.microservices.orderservice; import org.springframework.cloud.stream.function.StreamBridge; import org.springframework.stereotype.Component; @Component public class OrderProducer { private final StreamBridge streamBridge; public OrderProducer(StreamBridge streamBridge) { this.streamBridge = streamBridge; } public void sendOrderEvent(OrderEvent event) { // "output" matches the binding name in application.properties streamBridge.send("output", event); } }
- In
-
Add a REST controller to trigger events.
- In
OrderController.java
:package com.microservices.orderservice; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class OrderController { private final OrderProducer orderProducer; public OrderController(OrderProducer orderProducer) { this.orderProducer = orderProducer; } @PostMapping("/orders") public String createOrder(@RequestBody OrderEvent orderEvent) { orderProducer.sendOrderEvent(orderEvent); return "Order event sent for order ID: " + orderEvent.getOrderId(); } }
- In
-
Run
OrderService
.- From
OrderService
:./mvnw spring-boot:run
- It should start on port 8080 (by default).
- From
-
Test the event publishing.
- Use Postman or curl:
curl -X POST \ -H "Content-Type: application/json" \ -d '{"orderId":"123","status":"CREATED"}' \ http://localhost:8080/orders
- Check the console logs for any errors and confirm the event is produced to Kafka.
- Use Postman or curl:
- Generate a new Spring Boot project for
NotificationService
.- Artifact Id:
notification-service
- Dependencies:
- Spring Web
- Spring Cloud Stream
- Spring Cloud Stream Kafka Binder
- Artifact Id:
- Extract to
NotificationService
.
-
Import
NotificationService
into your IDE. -
Configure Spring Cloud Stream for Kafka.
- In
application.properties
:spring.application.name=notification-service spring.cloud.stream.kafka.binder.brokers=localhost:9092 spring.cloud.stream.bindings.input.destination=order-events
- This indicates the binding
input
is listening to the sameorder-events
topic.
- In
-
Create a message consumer.
- In
OrderEventConsumer.java
:package com.microservices.notificationservice; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import java.util.function.Consumer; @Component public class OrderEventConsumer { @Bean public Consumer<OrderEvent> input() { return orderEvent -> { System.out.println("Received order event: " + orderEvent); }; } }
- In
-
Create an event model.
- In
OrderEvent.java
(same as inOrderService
):package com.microservices.notificationservice; public class OrderEvent { private String orderId; private String status; // constructors, getters, setters, toString }
- In
-
Run
NotificationService
.- From
NotificationService
folder:./mvnw spring-boot:run
- It also starts on 8080 by default (or define another port in application.properties if you wish).
- From
-
Test the event-driven communication.
- Post an order event to
OrderService
:curl -X POST \ -H "Content-Type: application/json" \ -d '{"orderId":"456","status":"CREATED"}' \ http://localhost:8080/orders
- In the
NotificationService
logs, you should see something like:Received order event: OrderEvent{orderId='456', status='CREATED'}
- Post an order event to
-
Add a dead-letter topic.
- Configure a DLT for messages that cannot be processed.
-
Enhance the event model.
- Add fields (e.g.,
timestamp
,customerId
) toOrderEvent
in both producer and consumer to handle more complex data.
- Add fields (e.g.,
-
Simulate scaling.
- Run multiple
NotificationService
instances to observe how Kafka distributes messages.
- Run multiple
By completing this lab, you have:
- Installed and run Kafka locally (Zookeeper + Kafka broker).
- Built a producer (
OrderService
) that publishes messages to a topic (order-events
). - Created a consumer (
NotificationService
) that listens on the same topic. - Verified event-driven communication works seamlessly with Spring Cloud Stream.
This lays the foundation for decoupled microservices, letting them react to events asynchronously and scale independently.