# Arsitektur Event-Driven dengan Apache Kafka & Debezium CDC

> Diterbitkan oleh Literally Web ID pada 20 Oct 2026
> Sumber: https://literally.web.id/articles/arsitektur-event-driven-dengan-apache-kafka-debezium-cdc

---

## 1. Intisari Kilat: Fondasi dalam Sekali Lirik

Ingat malam-malam panjang ketika sistem web kita berantakan gara-gara *dual-write problem*? Saat backend PHP/Go/Node kita mencoba menyimpan data ke database PostgreSQL/MySQL, lalu di detik yang sama harus mengirim event ke Apache Kafka atau memanggil webhook pihak ketiga tanpa transaksi terdistribusi yang aman. Hasilnya? Data di database lolos, tapi event gagal terkirim karena jaringan ngadat atau PHP-FPM kehabisan worker. Pelanggan marah, data keranjang belanjaan hilang, dan kita harus lembur memperbaiki inkonsistensi data secara manual.

Di sinilah Change Data Capture (CDC) hadir sebagai penyelamat. Alih-alih melakukan *dual-write* dari aplikasi, kita membiarkan database bekerja seperti biasa, lalu menyadap *write-ahead log* (WAL) atau *binary log* menggunakan Debezium. Debezium membaca setiap perubahan baris secara real-time dan langsung mendorongnya ke Apache Kafka sebagai event stream yang bersih, terurut, dan terjamin tanpa menyentuh performa aplikasi utama kita barang sedetik pun.

> [!IMPORTANT]
> CDC bukanlah pengganti database transaksional, melainkan jembatan asinkron yang mengubah setiap *transaction commit* di disk menjadi event stream yang siap dikonsumsi oleh mikroservis, mesin pencari, atau cache Redis.

Memahami fondasi ini saja belum cukup untuk menyelamatkan infrastruktur kita dari bencana latensi tinggi jika salah memilih pola arsitektur sejak hari pertama perancangan sistem...

## 2. Kompas Keputusan: Kapan Memilih Solusi Ini vs Alternatif Lain

Sebagai arsitek yang sering menghadapi perdebatan sengit di ruang rapat teknis, pertanyaan klasik yang sering muncul adalah: "Kenapa tidak pakai *Application-level Publishing* atau *Transactional Outbox Pattern* saja? Kenapa harus repot-repot setup Debezium dan Kafka?"

Mari kita bedah melalui kompas keputusan berbasis skenario nyata di lapangan:

*   **Jika aplikasi Anda berskala kecil/menengah (traffic di bawah 500 req/sec) dengan stack monolitik berbasis Laravel/Node.js:** Jangan gunakan Debezium & Kafka. Gunakan **Transactional Outbox Pattern** di dalam tabel database yang sama, lalu jalankan *cron job* atau *supervisor worker* untuk mem-poll tabel outbox dan mengirimkannya ke antrean lokal Redis. Mengapa? Karena overhead operasional mengelola cluster Kafka dan Kafka Connect itu brutal dan memakan RAM server yang tidak sedikit.
*   **Jika Anda membangun ekosistem mikroservis terdistribusi berskala besar (traffic ribuan req/sec, polyglot backend Go, Node, PHP) yang butuh replikasi data real-time ke Elasticsearch, S3, dan layanan lain:** Hindari *Application-level Publishing*. Langsung pilih **Debezium CDC + Apache Kafka**. Solusi ini mengeliminasi risiko manusia (human error) di level koding aplikasi di mana developer sering lupa memanggil fungsi *publisher* setelah query SQL dieksekusi.
*   **Jika Anda terjebak dengan skenario *Dual-Write* langsung dari aplikasi (Simpan ke MySQL -> Kirim ke Kafka):** Hentikan sekarang juga. Ini adalah jalan tol menuju inkonsistensi data permanen saat jaringan *flaky* atau Kafka mengalami *rebalancing*.

Memilih senjata yang tepat baru separuh dari pertempuran; separuh sisanya adalah memastikan parameter konfigurasinya tidak meledakkan memori server produksi kita...

## 3. Contekan Konfigurasi & Parameter Paling Sering Dicari

Di lapangan, kegagalan Debezium sering kali bukan karena teknologinya yang buruk, melainkan salah menyetel parameter *offset storage*, *heartbeat*, dan *snapshot mode*. Berikut adalah contekan konfigurasi Kafka Connect untuk Debezium PostgreSQL Connector yang sudah teruji tahan banting di lingkungan produksi berbeban tinggi.

```json
{
  "name": "postgres-outbox-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "tasks.max": "1",
    "plugin.name": "pgoutput",
    "database.hostname": "postgres.internal",
    "database.port": "5432",
    "database.user": "debezium_user",
    "database.password": "SecretPass123!",
    "database.dbname": "production_db",
    "database.server.name": "pg_cluster_prod",
    "table.include.list": "public.orders,public.customers",
    
    "database.history.kafka.bootstrap.servers": "kafka-1.internal:9092,kafka-2.internal:9092",
    "database.history.kafka.topic": "schema-changes.production_db",
    
    "heartbeat.interval.ms": "10000",
    "max.batch.size": "2048",
    "max.queue.size": "8192",
    
    "tombstones.on.delete": "true",
    "decimal.handling.mode": "string",
    
    "transforms": "unwrap,route",
    "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
    "transforms.unwrap.drop.tombstones": "false"
  }
}
```

> [!TIP]
> Selalu gunakan `plugin.name: pgoutput` untuk PostgreSQL versi 14 ke atas karena menggunakan modul replikasi bawaan PostgreSQL tanpa perlu menginstal *plugin* pihak ketiga seperti `wal2json` yang sering bermasalah saat upgrade mayor database.

Konfigurasi di atas adalah amunisi harian kita, tetapi dalam operasional harian, kecepatan eksekusi terminal menggunakan satu baris perintah (one-liner) sering kali menjadi penentu hidup matinya investigasi insiden...

## 4. Koleksi Mantra One-Liner Siap Salin untuk Keseharian

Sebagai sysadmin dan backend engineer, kita tidak punya waktu untuk membuka antarmuka web yang lambat saat terjadi gangguan. Berikut adalah koleksi mantra terminal bash yang wajib ada di *history* laptop Anda:

*   **Memeriksa status Kafka Connect dan mengecek apakah connector sedang gagal (failed):**
    ```bash
    curl -s http://kafka-connect.internal:8083/connectors/postgres-outbox-connector/status | jq .
    ```

*   **Melihat stream event secara real-time langsung dari topik Kafka menggunakan console consumer:**
    ```bash
    kafka-console-consumer.sh --bootstrap-server kafka-1.internal:9092 --topic pg_cluster_prod.public.orders --from-beginning --property print.key=true
    ```

*   **Melakukan restart otomatis pada connector yang mengalami kegagalan akibat timeout jaringan:**
    ```bash
    curl -X POST http://kafka-connect.internal:8083/connectors/postgres-outbox-connector/restart
    ```

*   **Memeriksa lag (keterlambatan konsumsi) pada consumer group Kafka:**
    ```bash
    kafka-consumer-groups.sh --bootstrap-server kafka-1.internal:9092 --describe --group order-processing-service-group
    ```

> [!WARNING]
> Jangan pernah menjalankan perintah `--from-beginning` pada topik Kafka produksi berskala besar tanpa membatasi parameter `--max-messages`, atau terminal Anda akan mengalami *out of memory* karena memuat jutaan data historis sekaligus.

Kemudahan mantra-mantra terminal ini sering kali membuat kita lengah, padahal ada beberapa kesalahan fatal yang secara senyap dapat merusak seluruh kluster Kafka dan database operasional kita...

## 5. Jalur Bahaya: Dosa Besar yang Wajib Dihindari di Produksi

Pengalaman bertahun-tahun di infrastruktur skala besar mengajarkan saya bahwa kegagalan sistem jarang disebabkan oleh teknologi baru yang kompleks, melainkan oleh kebiasaan buruk yang dibiarkan menumpuk. Berikut adalah daftar dosa besar yang harus Anda hindari saat mengimplementasikan Debezium dan Kafka:

1.  **Membiarkan Tabel Tanpa Primary Key di PostgreSQL:** Debezium membutuhkan *Primary Key* atau *Replica Identity* unik untuk melacak perubahan baris (`UPDATE` dan `DELETE`). Jika tabel Anda tidak memiliki *Primary Key*, Debezium akan menerbitkan event tanpa kunci yang mengakibatkan kekacauan *partition routing* di Kafka.
2.  **Mengabaikan Pemantauan Disk WAL PostgreSQL:** CDC membaca Write-Ahead Log. Jika Kafka Connect mati berhari-hari sementara aplikasi terus menulis data ke database, WAL PostgreSQL akan membengkak tanpa batas hingga menghabiskan seluruh kapasitas disk server database Anda dan menyebabkan *database crash* total.
3.  **Tidak Menerapkan Idempotency pada Consumer:** Ingat hukum dasar *distributed systems*: jaringan bisa gagal, dan Kafka dapat mengirimkan pesan yang sama lebih dari sekali (*at-least-once delivery*). Jika backend Go atau Node Anda memproses pembayaran tanpa memeriksa apakah event ID tersebut sudah pernah dieksekusi, bersiaplah menghadapi amukan pelanggan karena saldo terpotong dua kali.
4.  **Menyalakan Snapshot Mode Sembarangan di Jam Sibuk:** Mengatur `snapshot.mode: initial` pada connector besar berukuran ratusan gigabyte di tengah jam operasional puncak akan membuat Debezium melakukan *full table scan*, mengunci tabel, dan melumpuhkan performa query aplikasi web utama Anda.