How to Implement Event-Driven Communication in Java Microservices

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.

🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.

▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube

Learn how to implement event-driven communication in Java microservices using Kafka. Step-by-step guide with real-world e-commerce example and full working code.

I am a bestseller Udemy Instructor. Check out my top 10 Udemy courses with discounts: My Udemy Courses — Ramesh Fadatare.

🧾 Introduction

Event-driven communication is the backbone of modern, scalable microservices architectures.

✅ Instead of tight synchronous REST calls, services communicate via events asynchronously.
✅ It improves scalability, resilience, and flexibility.

In this guide, you’ll learn how to implement Event-Driven Microservices using Kafka — through a real-world e-commerce example

✅ Project Setup: E-Commerce Microservices System

We’ll build a system with 3 services:

Communication Style: Services publish and listen to Kafka events.


📦 Step 1: Set Up Kafka Locally

You need a running Kafka instance. Use Docker Compose for simplicity.

# docker-compose.yml
version: '3'
services:
zookeeper:
image: wurstmeister/zookeeper
ports:
- "2181:2181"
kafka:
image: wurstmeister/kafka
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_HOST_NAME: localhost
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

➡️ Run:

docker-compose up

✅ Kafka broker running at localhost:9092.


📦 Step 2: Create Order Service (Producer)

2.1 Add Dependencies (Spring Boot + Kafka)

<!-- Order Service pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>

2.2 Configure Kafka Properties

Open the application.properties file and add the following properties:

# application.properties
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer

2.3 Create Event Class

public class OrderPlacedEvent {
private String orderId;
private String productId;
private int quantity;

public OrderPlacedEvent() {}

public OrderPlacedEvent(String orderId, String productId, int quantity) {
this.orderId = orderId;
this.productId = productId;
this.quantity = quantity;
}

// Getters and Setters
public String getOrderId() { return orderId; }
public void setOrderId(String orderId) { this.orderId = orderId; }

public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }

public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
}

2.4 Publish Event to Kafka

@Service
public class OrderService {

private final KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate;

public OrderService(KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}

public String placeOrder(String productId, int quantity) {
String orderId = UUID.randomUUID().toString();
OrderPlacedEvent event = new OrderPlacedEvent(orderId, productId, quantity);
kafkaTemplate.send("order-topic", event);
System.out.println("✅ OrderPlacedEvent published for Order ID: " + orderId);
return orderId;
}
}

2.5 Create REST API

Let’s build a simple REST API that internally calls OrderService and in turn OrderService publishes events to the Kafka topic.

@RestController
@RequestMapping("/api/orders")
public class OrderController {

private final OrderService orderService;

public OrderController(OrderService orderService) {
this.orderService = orderService;
}

@PostMapping
public ResponseEntity<String> placeOrder(@RequestBody OrderRequest request) {
String orderId = orderService.placeOrder(request.getProductId(), request.getQuantity());
return ResponseEntity.ok("✅ Order placed successfully. Order ID: " + orderId);
}
}

OrderRequest.java (DTO for incoming JSON)

public class OrderRequest {
private String productId;
private int quantity;

public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }

public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
}

2.6 Kafka Producer Configuration (Optional for JSON support)

If you want to send the OrderPlacedEvent as JSON, use this:

@Bean
public ProducerFactory<String, OrderPlacedEvent> producerFactory() {
Map<String, Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return new DefaultKafkaProducerFactory<>(config);
}

@Bean
public KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}

2.7 Sample API Test (Postman or Curl)

URL: POST http://localhost:8080/api/orders

Headers: Content-Type: application/json

Request Body:

{
"productId": "P-1001",
"quantity": 3
}

Expected Response:

Order placed successfully. Order ID: f4a8e9e0-712a-4c8e-a303-abc123456789

You should also see a log like:

✅ OrderPlacedEvent published for Order ID: f4a8e9e0-712a-4c8e-a303-abc123456789

📦 Step 3: Create Payment Service (Consumer + Producer)

3.1 Add Dependencies

<!-- Payment Service pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
</dependencies>

3.2 Kafka Config

spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=payment-service-group
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer

3.3 Listen to OrderPlacedEvent and Publish PaymentCompletedEvent

@Service
public class PaymentService {

private final KafkaTemplate<String, PaymentCompletedEvent> kafkaTemplate;

public PaymentService(KafkaTemplate<String, PaymentCompletedEvent> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}

@KafkaListener(topics = "order-topic", groupId = "payment-service-group")
public void processPayment(OrderPlacedEvent event) {
System.out.println("Processing payment for Order ID: " + event.getOrderId());

// Simulate payment success
PaymentCompletedEvent paymentCompleted = new PaymentCompletedEvent(event.getOrderId(), "SUCCESS");
kafkaTemplate.send("payment-topic", paymentCompleted);

System.out.println("PaymentCompletedEvent published for Order ID: " + event.getOrderId());
}
}

PaymentCompletedEvent

public class PaymentCompletedEvent {
private String orderId;
private String status;

// Constructors, Getters, Setters
}

📦 Step 4: Create Inventory Service (Consumer)

4.1 Add Dependencies and Kafka Config (Same as before)

<!-- Inventory Service pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
</dependencies>

4.2 Listen to PaymentCompletedEvent

@Service
public class InventoryService {

@KafkaListener(topics = "payment-topic", groupId = "inventory-service-group")
public void reserveStock(PaymentCompletedEvent event) {
if ("SUCCESS".equals(event.getStatus())) {
System.out.println("Reserving stock for Order ID: " + event.getOrderId());
// Inventory logic here
} else {
System.out.println("Payment failed. No stock reserved for Order ID: " + event.getOrderId());
}
}
}

✅ Inventory only reserves stock if payment is successful.

🏗️ Overall Architecture

✅ Full Event-Driven asynchronous communication
✅ No tight service-to-service calls
✅ Each service is independent but coordinated via events

✅ Final Thoughts

✅ Event-Driven Architecture with Kafka decouples services.
✅ It makes Java microservices scalable, resilient, and independent.
✅ Each service focuses only on its domain — communicating via reliable, asynchronous events.

Good developers build services.
Great developers make services work together smartly with events.

Master event-driven design — and your Java microservices will scale effortlessly in real-world production.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare