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)