DEV Community

Dev Cookies
Dev Cookies

Posted on

πŸ”„ Service-to-Service Communication in Spring Boot using Kafka (with Spring Cloud Stream)

πŸš€ Introduction

In a microservices architecture, services often need to communicate with each other. While synchronous communication using REST APIs is common, asynchronous communication using Kafka is preferred for event-driven and loosely coupled systems.

In this blog, we’ll explore how to implement service-to-service communication using Apache Kafka and Spring Cloud Stream in Spring Boot applications.


🧩 Tech Stack

  • Java 17+
  • Spring Boot 3.x
  • Spring Cloud Stream
  • Apache Kafka
  • Spring Cloud Function (Optional)

πŸ“¦ Project Overview

We'll build two microservices:

  • Order Service – Publishes events to Kafka when a new order is created.
  • Inventory Service – Listens to those events and updates the stock.

πŸ“ Dependencies (Add to both services)

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-kafka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

Add the Spring Cloud BOM in pom.xml:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2023.0.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
Enter fullscreen mode Exit fullscreen mode

🧾 1. Order Service (Producer)

βœ… application.yml

spring:
  application:
    name: order-service
  cloud:
    stream:
      bindings:
        order-out:
          destination: order-topic
          content-type: application/json
          producer:
            required-groups: inventory-group
      kafka:
        binder:
          brokers: localhost:9092
Enter fullscreen mode Exit fullscreen mode

βœ… Order Model

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private String orderId;
    private String productId;
    private int quantity;
}
Enter fullscreen mode Exit fullscreen mode

βœ… Order Publisher

@Service
@RequiredArgsConstructor
public class OrderService {

    private final StreamBridge streamBridge;

    public void placeOrder(Order order) {
        streamBridge.send("order-out", order);
        System.out.println("πŸ“€ Sent Order to Kafka: " + order);
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… REST Controller

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

    private final OrderService orderService;

    @PostMapping
    public ResponseEntity<String> createOrder(@RequestBody Order order) {
        orderService.placeOrder(order);
        return ResponseEntity.ok("Order placed successfully!");
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“¦ 2. Inventory Service (Consumer)

βœ… application.yml

spring:
  application:
    name: inventory-service
  cloud:
    stream:
      bindings:
        order-in:
          destination: order-topic
          group: inventory-group
          content-type: application/json
      kafka:
        binder:
          brokers: localhost:9092
Enter fullscreen mode Exit fullscreen mode

βœ… Inventory Listener

@Component
public class InventoryConsumer {

    @StreamListener("order-in")
    public void handleOrder(Order order) {
        System.out.println("πŸ“₯ Received Order in Inventory: " + order);
        // Simulate inventory update
        System.out.println("βœ… Inventory updated for product: " + order.getProductId());
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… You can also use @Bean with functional style instead of @StreamListener:

@Bean
public Consumer<Order> orderConsumer() {
    return order -> {
        System.out.println("πŸ“₯ [Functional] Received Order: " + order);
    };
}
Enter fullscreen mode Exit fullscreen mode

πŸ› οΈ Kafka Setup (Local)

  1. Start Zookeeper & Kafka:
# Start Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties

# Start Kafka Server
bin/kafka-server-start.sh config/server.properties
Enter fullscreen mode Exit fullscreen mode
  1. Create Topic:
bin/kafka-topics.sh --create --topic order-topic --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Testing

  1. Run both services.
  2. Make a POST call to localhost:8080/orders:
POST /orders
Content-Type: application/json

{
  "orderId": "O101",
  "productId": "P5001",
  "quantity": 3
}
Enter fullscreen mode Exit fullscreen mode
  1. Output:
  • Order Service: πŸ“€ Sent Order to Kafka
  • Inventory Service: πŸ“₯ Received Order in Inventory

🧠 Why Use Spring Cloud Stream?

Feature Benefit
Abstraction over Kafka Write less boilerplate code.
Flexible Binding Easily switch between Kafka, RabbitMQ, etc.
Declarative Configuration Use YAML to configure consumers/producers.
Built-in Serialization JSON, Avro, etc. with minimal setup.

βš–οΈ Kafka vs REST for Microservices

REST (Synchronous) Kafka (Asynchronous)
Simple & direct Decoupled services
Tight coupling Loose coupling
Fails if target down Can retry later
Real-time request/response Event-driven patterns

βœ… Best Practices

  • βœ… Always define a consumer group.
  • βœ… Handle serialization errors using ErrorHandlingDeserializer.
  • βœ… Use dead-letter queues for failed messages.
  • βœ… Monitor with Kafka Manager or Confluent Control Center.
  • βœ… Use Avro/Schema Registry for contract enforcement.

πŸ“Œ Conclusion

Using Spring Cloud Stream with Kafka is a powerful way to enable asynchronous service-to-service communication. It abstracts low-level Kafka configurations and allows you to focus on business logic.

πŸ” Whether you're building event-driven microservices or improving scalability, this architecture ensures decoupling, flexibility, and resilience.


\

Top comments (0)