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
-
Producer →
KafkaTemplate<K,V>(digunakan untuk mengirim data) -
Consumer →
@KafkaListener(topics="...")atauKafkaMessageListenerContainer -
Topic → property di config / pembuatan via admin (
NewTopic) atau admin client -
Serializer / Deserializer →
StringSerializer,StringDeserializer,JsonSerializer,JsonDeserializer -
ConsumerFactory / ProducerFactory → fabrika untuk membuat consumer/producer yang digunakan
KafkaTemplatedan listener container -
ConcurrentKafkaListenerContainerFactory → konfigurasi listener container (concurrency / error handler)
-
Acknowledgment → mekanisme manual ack (
Acknowledgmentparameter) 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:
-
Client
POST /orders→ Order service menerima request. -
Order service publish event
OrderCreatedke Kafka topicorders. -
Consumer service (bisa same app atau service terpisah) mendengarkan topic
ordersdan 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 |
7) Siapkan Kode dan Spring Boot
<!-- Apache Kafka integration -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
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
└── yourprojectname/
└── kafka/
├── config/
│ ├── KafkaProducerConfig.java
│ └── KafkaConsumerConfig.java
├── producer/
│ └── MessageProducer.java
├── consumer/
│ └── MessageConsumer.java
└── controller/
└── MessageController.java
- 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.
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());
}
}
- 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.
- ConcurrentKafkaListenerContainerFactory: Mengelola listener container agar aplikasi bisa menerima pesan dari Kafka secara paralel.
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;
}
}
- Terdapat konstanta TOPIC yang menentukan nama topik Kafka.
- KafkaTemplate<String, String> di-inject melalui konstruktor.
- Metode sendMessage digunakan untuk mengirim pesan ke topik yang sudah ditentukan dan menampilkan konfirmasi di konsol.
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);
}
}
- 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.
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);
}
}
- 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.
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
-
Jalankan Docker Compose (
docker-compose up -d) → pastikan Kafka reachable dilocalhost:9092. -
Jalankan Spring Boot app (Spring Boot run).
-
POST event:
-
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.idberbeda 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=alluntuk durability terbaik;acks=0paling 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
KafkaAdminuntuk 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:
KafkaTemplateuntuk produce,@KafkaListeneruntuk consume, dan konfigurasi serializer/deserializer untuk JSON.

Comments
Post a Comment