Kamis, 12 Juni 2025

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

| Kamis, 12 Juni 2025

๐Ÿš€ 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>

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>

๐Ÿงพ 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

✅ Order Model

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

✅ 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);
    }
}

✅ 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!");
    }
}

๐Ÿ“ฆ 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

✅ 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());
    }
}

✅ 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);
    };
}

๐Ÿ› ️ 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
  1. Create Topic:
bin/kafka-topics.sh --create --topic order-topic --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1

๐Ÿงช 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
}
  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.

\


Related Posts

Tidak ada komentar:

Posting Komentar