Memahami Peran Apache Kafka dalam Event Driven Architecture dengan Spring Boot

Dalam dunia sistem terdistribusi modern, Event-Driven Architecture (EDA) telah menjadi pendekatan yang semakin populer untuk membangun aplikasi yang responsif, skalabel, dan loosely coupled. Salah satu teknologi inti yang mendukung pola ini adalah Apache Kafka, sebuah platform streaming yang dirancang untuk menangani aliran data secara real-time dengan keandalan tinggi. Dengan memanfaatkan Kafka, setiap komponen aplikasi dapat berkomunikasi melalui event, bukan panggilan langsung, sehingga sistem menjadi lebih fleksibel dan mudah dikembangkan. Artikel ini akan membahas secara komprehensif peranan Kafka dalam arsitektur event-driven menggunakan Spring Boot — dimulai dari pemahaman arsitektur Kafka, komponen dan terminologinya, hingga implementasi konkret berbasis Spring Boot yang dapat kamu ikuti langkah demi langkah. Tujuannya adalah agar pembaca tidak hanya memahami konsep, tetapi juga mampu menerapkannya secara praktis dalam pengembangan aplikasi berbasis event.

Peranan Kafka pada Event-Driven Architecture + contoh Spring Boot langkah-demi-langkah

Kita akan membahas dari konsep sampai implementasi konkret. Saya susun supaya  bisa:

1). Memahami arsitekturnya

2). Tahu istilah-istilah penting 

3). Paham bagaimana Spring Boot memetakan konsep Kafka

4). Mencoba contoh sederhana: sebuah service kecil yang menerbitkan event (producer) melalui REST dan menerima event (consumer) via @KafkaListener.

1) kenapa Kafka di Event-Driven?

  • Kafka adalah distributed event streaming platform: tahan banting, skalabel, cocok untuk menyimpan dan menyebarkan aliran (streams) event secara real-time.

  • Dalam arsitektur event-driven, komponen (microservices) saling berkomunikasi lewat event (mis. OrderCreated) — mereka tidak memanggil satu sama lain secara sinkron.

  • Kafka bertindak sebagai durable message broker / event log: producer menulis event ke topic, consumer membaca event secara independen, dapat di-replay, dan berskala.

2) Arsitektur Kafka - komponen inti 

  • Broker: proses Kafka (server). Cluster Kafka = beberapa broker. Broker menyimpan data topic.

  • Topic: kanal topik log (mis. orders). Topic dibagi menjadi partitions.

  • Partition: unit paralelisme. Setiap partition adalah urutan berurutan pesan (append-only log).

  • Offset: posisi/indeks pesan dalam partition. Consumer menyimpan offset untuk menandai progres.

  • Producer: aplikasi yang menulis pesan ke topic.

  • Consumer: aplikasi yang membaca pesan dari topic.

  • Consumer group: satu grup konsumen yang berbagi konsumsi pesan dari partitions — tiap partition hanya dilayani oleh satu consumer dalam grup (scaling).

  • Replication: setiap partition punya replica untuk toleransi kegagalan (leader + followers).

  • ZooKeeper / KRaft: Historically Kafka memakai ZooKeeper untuk metadata; modern Kafka menyediakan mode KRaft (cluster metadata tanpa ZooKeeper) — versi/opsi berbeda tergantung rilis Kafka yang dipakai.

  • Controller (leader election), ISR (in-sync replicas) — detail operasional untuk HA.

3) Istilah penting singkat

  • acks: pengaturan pada producer (ack=all/1/0) menentukan jaminan durabilitas.

  • partition key: menentukan ke partition mana pesan akan masuk (hash key).

  • exactly-once semantics (EOS): tersedia via producer transactions & broker support.

  • retention: berapa lama log disimpan (time atau size).

  • compact: mode topic kompaksi untuk state store.

4) Istilah Kafka → mapping di Spring for Apache Kafka

  • ProducerKafkaTemplate<K,V> (digunakan untuk mengirim data)

  • Consumer@KafkaListener(topics="...") atau KafkaMessageListenerContainer

  • Topic → property di config / pembuatan via admin (NewTopic) atau admin client

  • Serializer / DeserializerStringSerializer, StringDeserializer, JsonSerializer, JsonDeserializer

  • ConsumerFactory / ProducerFactory → fabrika untuk membuat consumer/producer yang digunakan KafkaTemplate dan listener container

  • ConcurrentKafkaListenerContainerFactory → konfigurasi listener container (concurrency / error handler)

  • Acknowledgment → mekanisme manual ack (Acknowledgment parameter) atau auto

  • KafkaAdmin → bean untuk membuat topic secara otomatis pada startup (opsional)

  • Consumer group → property group.id


5) Contoh kasus sederhana untuk belajar

Use case: Order processing (demo)
Flow minimal:

  1. Client POST /orders → Order service menerima request.

  2. Order service publish event OrderCreated ke Kafka topic orders.

  3. Consumer service (bisa same app atau service terpisah) mendengarkan topic orders dan memproses order (mis. simulasikan invetory check / log).

Kenapa ini bagus:

  • Realistis (common pattern)

  • Memperlihatkan producer + consumer + topic + partition + consumer group

  • Mudah diikuti secara step-by-step

6) Setup praktis Docker

Struktur contoh: satu Spring Boot app yang berperan sebagai producer (REST → publish) dan juga consumer (mendengarkan topic dan memproses). Cocok untuk demo lokal.

6.1 Siapkan docker-compose.yml

# Kita pakai versi file compose v3 (stabil dan umum digunakan)
version: '3'

# Bagian utama yang berisi semua service/container yang akan dijalankan
services:

# ==========================
# 1️ ZOOKEEPER
# ==========================
zookeeper:
# Menggunakan image resmi dari Confluent (vendor utama Kafka)
image: confluentinc/cp-zookeeper:7.5.0

# Nama container di Docker (optional, tapi membantu debugging)
container_name: zookeeper

# Port 2181 adalah port default Zookeeper
ports:
- "2181:2181"

# Environment variable untuk konfigurasi Zookeeper
environment:
# Port tempat client Kafka berkomunikasi dengan Zookeeper
ZOOKEEPER_CLIENT_PORT: 2181
# Interval sinkronisasi antara Zookeeper leader dan follower (dalam ms)
ZOOKEEPER_TICK_TIME: 2000

# ==========================
# 2️ KAFKA BROKER
# ==========================
kafka:
# Image resmi Kafka dari Confluent Platform
image: confluentinc/cp-kafka:7.5.0

# Nama container Kafka
container_name: kafka

# Port 9092 digunakan oleh aplikasi (seperti Spring Boot) untuk koneksi ke Kafka
ports:
- "9092:9092"

# Dependensi — Kafka harus menunggu Zookeeper siap dulu sebelum start
depends_on:
- zookeeper

# Environment variable penting untuk konfigurasi Kafka broker
environment:
# ID unik broker Kafka di dalam cluster (kalau ada beberapa broker)
KAFKA_BROKER_ID: 1

# Alamat Zookeeper yang akan dihubungi oleh Kafka
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

# Listener menentukan bagaimana Kafka mengekspose dirinya ke luar container
# PLAINTEXT://localhost:9092 berarti aplikasi di host dapat mengakses Kafka lewat localhost:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092

# Jumlah replika minimum untuk topik internal (offset, config, dsb.)
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

# ==========================
# 3️ KAFKA UI (OPSIONAL, UNTUK MONITORING)
# ==========================
kafka-ui:
# Image web UI ringan untuk melihat topik, consumer, dan pesan secara visual
image: provectuslabs/kafka-ui:latest

# Nama container
container_name: kafka-ui

# Kafka UI akan berjalan di port 8081
ports:
- "8081:8080"

# Kafka UI perlu tahu di mana Kafka Broker berada
environment:
- KAFKA_CLUSTERS_0_NAME=local
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9092
- KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper:2181

# Kafka UI baru bisa jalan setelah Kafka aktif
depends_on:
- kafka

 6.2 Pastikan Docker sedang berjalan

ss@yusufibrahim:~/IdeaProjects/YBoilerplate$ docker --version
Docker version 28.5.1, build e180ab8 

 6.2 Jalankan Semua Container

ss@yusufibrahim:~/IdeaProjects/YBoilerplate$ docker compose up -d   

Penjelasan:

  • up : menjalankan container sesuai definisi di file Compose

  • -d : detached mode, artinya berjalan di background (tidak menempel di terminal)

6.2 Kafka UI di http://localhost:8081

Jika berhasil, kamu akan melihat tampilan Kafka UI seperti berikut:

  • Nama cluster: local

  • Broker: kafka:9092

  • Kamu bisa lihat daftar topic, consumer group, dan pesan yang dikirim dari Spring Boot-mu.

Kafka UI 

6.3 Melihat Log Container (Opsional)
 
Kalau kamu ingin melihat log Kafka atau consumer:
$ docker logs kafka -f
 
Atau log Zookeeper:
$ docker logs zookeeper -f 

7) Siapkan Kode dan Spring Boot

7.1 Update pom.xml dan tambahkan spring-kafka:
<!-- Apache Kafka integration -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency> 
 7.2 Konfigurasi Kafka di src/main/resources/application.properties
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=yboiler-group
spring.kafka.consumer.auto-offset-reset=earliest
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
7.3  Tambahkan Package Baru
 
 com/
 └── yourprojectname/
     └── kafka/
         ├── config/
         │   ├── KafkaProducerConfig.java
         │   └── KafkaConsumerConfig.java
         ├── producer/
         │   └── MessageProducer.java
         ├── consumer/
         │   └── MessageConsumer.java
         └── controller/
             └── MessageController.java
 
7.3.1 KafkaProducerConfig.java
 
KafkaProducerConfig adalah kelas konfigurasi di Spring Boot yang digunakan untuk mengatur produser Kafka. 
Kelas ini membuat dua bean utama:
  • ProducerFactory: Mengatur properti koneksi ke broker Kafka (seperti alamat server, serializer untuk key dan value). Factory ini bertanggung jawab membuat instance produser Kafka.
  • KafkaTemplate: Abstraksi yang memudahkan pengiriman pesan ke Kafka. Template ini menggunakan ProducerFactory yang sudah dikonfigurasi.
Cara kerjanya:
Saat aplikasi dijalankan, Spring Boot akan membaca konfigurasi ini, membuat bean yang diperlukan, dan memungkinkan bagian lain dari aplikasi untuk mengirim pesan ke Kafka dengan mudah menggunakan KafkaTemplate. 
 
package com.mhyusuf.yboilerplate.kafka.config;

import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class KafkaProducerConfig {

@Bean
public ProducerFactory<String, String> 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, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(config);
}

@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}
 
7.3.2 KafkaConsumerConfig.java
 
KafkaConsumerConfig adalah kelas konfigurasi di Spring Boot yang digunakan untuk mengatur konsumen Kafka. Kelas ini membuat dua bean utama:
  1. ConsumerFactory: Mengatur properti koneksi ke broker Kafka (seperti alamat server, group id, dan deserializer untuk key dan value). Factory ini bertanggung jawab membuat instance konsumen Kafka.
  2. ConcurrentKafkaListenerContainerFactory: Mengelola listener container agar aplikasi bisa menerima pesan dari Kafka secara paralel.
Hubungannya dengan KafkaProducerConfig adalah:
KafkaProducerConfig mengatur produser untuk mengirim pesan ke Kafka, sedangkan KafkaConsumerConfig mengatur konsumen untuk menerima pesan dari Kafka. Keduanya saling terhubung melalui broker dan topik yang sama, sehingga pesan yang dikirim produser bisa diterima oleh konsumen. 
package com.mhyusuf.yboilerplate.kafka.config;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;

import java.util.HashMap;
import java.util.Map;

@EnableKafka
@Configuration
public class KafkaConsumerConfig {

@Bean
public ConsumerFactory<String, String> consumerFactory() {
Map<String, Object> config = new HashMap<>();
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
config.put(ConsumerConfig.GROUP_ID_CONFIG, "yboiler-group");
config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
return new DefaultKafkaConsumerFactory<>(config);
}

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
return factory;
}
}
 
7.3.3 MessageProducer.java
MessageProducer.java adalah kelas layanan di Spring Boot yang bertugas untuk mengirim pesan ke topik Kafka. Kelas ini menggunakan KafkaTemplate untuk berinteraksi dengan Kafka.
  1. Terdapat konstanta TOPIC yang menentukan nama topik Kafka.
  2. KafkaTemplate<String, String> di-inject melalui konstruktor.
  3. Metode sendMessage digunakan untuk mengirim pesan ke topik yang sudah ditentukan dan menampilkan konfirmasi di konsol.
Kelas ini biasanya dipanggil oleh bagian lain aplikasi untuk mempublikasikan pesan ke Kafka. 
package com.mhyusuf.yboilerplate.kafka.producer;

import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
public class MessageProducer {

private static final String TOPIC = "yboiler-topic";

private final KafkaTemplate<String, String> kafkaTemplate;

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

public void sendMessage(String message) {
kafkaTemplate.send(TOPIC, message);
System.out.println(" Sent message: " + message);
}
}
7.3.4 MessageConsumer.java
MessageConsumer.java adalah kelas layanan di Spring Boot yang berfungsi untuk menerima pesan dari topik Kafka. Biasanya, kelas ini menggunakan anotasi @KafkaListener untuk mendengarkan pesan yang masuk pada topik tertentu.
  1. Kelas ini akan otomatis aktif saat aplikasi berjalan dan menunggu pesan dari Kafka.
  2. Ketika ada pesan baru di topik yang didengarkan, metode yang diberi anotasi @KafkaListener akan dipanggil dan pesan akan diproses sesuai kebutuhan aplikasi.
Kelas ini melengkapi MessageProducer.java, sehingga aplikasi bisa mengirim dan menerima pesan melalui Kafka. 
package com.mhyusuf.yboilerplate.kafka.consumer;

import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Service
public class MessageConsumer {

@KafkaListener(topics = "yboiler-topic", groupId = "yboiler-group")
public void consume(String message) {
System.out.println("📩 Received message: " + message);
}
}
7.3.5 MessageController.java
MessageConsumer.java adalah kelas layanan di Spring Boot yang berfungsi untuk menerima pesan dari topik Kafka. Biasanya, kelas ini menggunakan anotasi @KafkaListener untuk mendengarkan pesan yang masuk pada topik tertentu.
  • Kelas ini akan otomatis aktif saat aplikasi berjalan dan menunggu pesan dari Kafka.
  • Ketika ada pesan baru di topik yang didengarkan, metode yang diberi anotasi @KafkaListener akan dipanggil dan pesan akan diproses sesuai kebutuhan aplikasi.
Kelas ini melengkapi MessageProducer.java, sehingga aplikasi bisa mengirim dan menerima pesan melalui Kafka.
package com.mhyusuf.yboilerplate.kafka.controller;

import com.mhyusuf.yboilerplate.kafka.producer.MessageProducer;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/messages")
public class MessageController {

private final MessageProducer producer;

public MessageController(MessageProducer producer) {
this.producer = producer;
}

@PostMapping
public String sendMessage(@RequestParam String msg) {
producer.sendMessage(msg);
return "Message sent to Kafka topic!";
}
} 

8) Uji Coba Endpoint

  1. Jalankan Docker Compose (docker-compose up -d) → pastikan Kafka reachable di localhost:9092.

  2. Jalankan Spring Boot app (Spring Boot run).

  3. POST event:

$ curl -X POST "http://localhost:8080/api/messages?msg=Halo%20dari%20YBoiler!"
 
  1. Lihat console app — consumer akan menerima dan log pesan. Jika consumer berjalan di proses yang sama, kamu lihat pesan masuk. Jika consumer di proses lain, pastikan group.id berbeda jika mau membaca semua pesan (atau sama kalau mau scale).  

9) Penjelasan detail yg sering bikin bingung (FAQ singkat)

  • Mengapa ada partitions?  untuk paralelisme: lebih banyak partition = lebih banyak consumer concurrent di dalam group.

  • Bagaimana konsistensi order?  pesan pada satu partition terjaga urutannya; jika ingin order global, gunakan 1 partition (trade-off throughput).

  • Producer acks  acks=all untuk durability terbaik; acks=0 paling cepat tapi berisiko.

  • Consumer offset  bisa auto-commit atau manual commit via Acknowledgment. Untuk pemrosesan idempotent, lebih aman commit setelah pemrosesan selesai.

  • Exactly once?  memungkinkan via transactions (producer transactions + broker support) dan idempotent consumers, tapi kompleks.

  • Error handling  gunakan SeekToCurrentErrorHandler / DefaultErrorHandler (Spring Kafka) untuk retry/dlq handling.

10) Tips & Best practices singkat

  • Gunakan key untuk menentukan partitioning bila butuh affinity (mis. semua event untuk user X ke partition sama).

  • Buat consumer idempotent (agar reprocessing tidak merusak state).

  • Jangan gunakan topic retention sangat pendek bila kamu butuh replay.

  • Gunakan compaction untuk topic yang menyimpan latest state per key.

  • Gunakan KafkaAdmin untuk mengelola topik di infra as code.

  • Untuk production: konfigurasi replica >1, acks=all, monitor lag, setup security (SASL/SSL), set retention & quotas.

11) Ringkasan Penutup

  • Kafka = distributed, durable, partitioned, replicated event log.

  • Producer menulis event ke topic; consumer membaca event dari topic secara independen.

  • Partition memberi paralelisme; offset menandai posisi baca.

  • Dalam Spring Boot: KafkaTemplate untuk produce, @KafkaListener untuk consume, dan konfigurasi serializer/deserializer untuk JSON.




Comments

Popular posts from this blog

Numpang Kerja Remote dari Bandung Creative Hub

Numpang Kerja Remote dari Bandung Digital Valley

Cara Decompile berkas Dex dan Apk Android