Episode ini membahas peran Kafka dalam microservices: event-driven architecture, choreography versus orchestration, domain events, pola saga dengan compensating transactions, CQRS dengan materialized views, serta transactional outbox dan integrasi dengan Debezium.

Ketika kalian memecah monolit menjadi microservices, masalah paling sulit bukan kode, melainkan komunikasi. Request-response sinkron membuat service saling menunggu, dan transaksi lintas service hampir mustahil dijaga atomic. Kafka menawarkan jalan keluar dengan memindahkan komunikasi ke event.
Episode 26 ini akan membahas bagaimana Kafka menjadi tulang punggung arsitektur event-driven: pola choreography dan orchestration, domain events, pola saga untuk transaksi terdistribusi, CQRS dengan materialized views, serta transactional outbox untuk menghubungkan database dan Kafka secara andal.
Dalam arsitektur event-driven, service berkomunikasi lewat event, bukan panggilan langsung. Service publisher menulis event ke topic; service lain berlangganan tanpa mengetahui satu sama lain. Keuntungan utama: decoupling — publisher tidak peduli siapa konsumennya, dan menambah konsumen tidak mengubah publisher.
Pilih choreography untuk alur sederhana yang independen; pilih orchestration ketika urutan dan kontrol penting.
Event storming adalah workshop untuk memetakan peristiwa bisnis menjadi domain events — misalnya OrderCreated, PaymentCompleted, InventoryReserved. Setiap event menjadi kandidat record Kafka. Domain events harus menggunakan bahasa domain bisnis (bukan teknis), sehingga menjadi kontrak bersama antar service.
Saat mendesain topic untuk microservices, jangan pernah membuat satu topic "super" yang berisi semua event semua domain. Pisahkan berdasarkan domain dan versi, misalnya order-events dan payment-events, dan gunakan Schema Registry (episode 7) agar kontrak event bisa berevolusi tanpa memecah konsumen.
Transaksi bisnis sering melintasi beberapa service: pesan, bayar, kurangi stok. Menyimpan atomicity lintas database berbeda butuh pola saga: rangkaian langkah lokal, setiap langkah menulis database sendiri dan menerbitkan event. Kafka menyediakan kanal event untuk mengoordinasikan langkah-langkah ini.
Ketika sebuah langkah gagal, saga harus memutar balik langkah yang sudah berhasil — dengan compensating transactions. Contoh: jika pembayaran gagal setelah stok dikurangi, jalankan kompensasi mengembalikan stok:
OrderService: terima order -> publish OrderCreated
PaymentService: proses bayar -> publish PaymentCompleted
InventoryService: kurangi stok -> publish InventoryReserved
Jika gagal: publish PaymentFailed -> kompensasi InventoryRestockedPaymentFailed memicu kompensasi; service lain merespons dengan event pembalik. Saga orchestrated bisa dijalankan dengan orchestrator yang membaca status dari topic dan mengirim perintah berikutnya.
CQRS memisahkan operasi tulis (command) dan baca (query) ke model berbeda. Kafka mendukungnya secara alami: command service menulis event ke topic, sementara query service membangun projection dari event untuk melayani pembacaan cepat. Data tulis dan baca tidak perlu berada di database yang sama.
Dengan event sourcing, semua perubahan state disimpan sebagai rangkaian event — sumber kebenaran tunggal. Materialized view adalah proyeksi state saat ini yang dibangun dari event. Kafka Streams (episode 13) menjadi alat ideal: setiap consumer membangun view-nya sendiri dari topic yang sama tanpa menyentuh database command service.
KTable<String, OrderState> orders = builder
.stream("order-events")
.groupByKey()
.aggregate(OrderState::new,
(key, event, state) -> state.apply(event),
Materialized.as("order-view-store"));Materialized.as("order-view-store") membangun projection per order yang selalu terbarui — query service cukup membaca state store (interactive queries di episode 13) tanpa mengetuk database lain.
Bagaimana menjamin event terkirim tepat saat database berubah? Menulis database lalu mengirim event punya celah: crash di antara keduanya membuat state dan event tidak konsisten. Transactional outbox memecahkannya: tulis perubahan database dan record outbox dalam satu transaksi database:
BEGIN;
INSERT INTO orders (id, status) VALUES ('order-001', 'CREATED');
INSERT INTO outbox (event_id, topic, payload, created_at)
VALUES (uuid, 'order-events', '{"id":"order-001"}', now());
COMMIT;BEGIN; ... COMMIT; menjamin record outbox hanya ada jika perubahan utama berhasil. Sebuah relay kemudian membaca outbox dan menerbitkan ke Kafka — jika crash, outbox belum terbaca dan akan dikirim ulang, memberikan at-least-once yang andal.
Alih-alih menulis relay sendiri, pakai CDC (Change Data Capture): Debezium membaca log transaksi database dan menerbitkan perubahan — termasuk baris outbox — ke Kafka secara otomatis. Detail lengkap akan dibahas di episode 27. Kombinasi transactional outbox plus Debezium adalah standar industri untuk integrasi database-Kafka yang tidak kehilangan event.
Warning
Saga memberikan konsistensi eventual, bukan atomic. Tidak ada jaminan semua langkah selesai bersama-sama; yang dijamin adalah sistem mencapai state konsisten melalui kompensasi. Rancang kompensasi untuk setiap langkah saga sejak awal.
Di episode 26 ini kalian sudah memahami peran Kafka dalam microservices: event-driven architecture dengan choreography dan orchestration, domain events, pola saga dengan compensating transactions, CQRS dengan materialized views, serta transactional outbox dengan Debezium.
Inti yang harus dibawa pulang:
Di episode 27 selanjutnya kita akan membahas Change Data Capture dengan Debezium — membaca perubahan database menjadi aliran event, arsitektur Debezium, konfigurasi connector, snapshot mode, serta best practices untuk schema evolution dan monitoring lag.