# Event-Driven Architecture: Kapan Sebaiknya TIDAK Dipakai

> Event-driven architecture menyelesaikan masalah nyata, tapi bisa diam-diam merusak sistem kecil. Kriteria keputusan jujur dari ERP yang dibangun dengan cara membosankan dulu.

URL: https://www.matthewswong.com/id/blog/event-driven-architecture-when-not-to/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-04-02
- Updated: 2026-04-02
- Category: Software Development

Event-driven architecture adalah pola yang semua orang ingin cantumkan di CV tapi hampir tidak pernah dipertanyakan. Janjinya menggoda: service yang decoupled, skalabilitas tanpa batas, sistem yang bereaksi terhadap dunia alih-alih melakukan polling. Saya sudah merilis alur event-driven di production dan akan terus melakukannya. Tapi saya juga pernah melihat tim, termasuk diri saya di masa lalu, mengambil sistem CRUD yang mudah dipahami lalu mengubahnya menjadi misteri pembunuhan terdistribusi di mana tidak ada yang bisa menjawab pertanyaan operasional paling sederhana: apa yang terjadi, dalam urutan apa, dan kenapa.

Tulisan ini adalah kerangka keputusan yang dulu saya harap ada yang memberikannya sebelum saya menyambungkan message broker pertama ke sebuah ERP. Jawaban singkatnya di awal: kalau sistem Anda muat di satu database, tim Anda muat di satu ruangan, dan workflow Anda butuh jawaban sekarang bukan nanti, kemungkinan besar Anda belum butuh event. Begini cara memastikannya.

## Apa yang Sebenarnya Anda Dapat dari Event

Mari adil dulu terhadap polanya. Tulisan klasik Martin Fowler membedakan event notification, event-carried state transfer, event sourcing, dan CQRS, dan masing-masing menyelesaikan masalah coupling yang spesifik. Event notification membuat producer bisa mengumumkan bahwa sesuatu terjadi tanpa perlu tahu siapa yang mendengarkan. Event-carried state transfer membuat consumer menyimpan salinan datanya sendiri sehingga bisa menjawab query tanpa memanggil sistem sumber. Ini properti yang nyata dan berharga ketika Anda punya banyak tim dan banyak service yang saling bersinggungan.

AWS membingkai ide yang sama secara operasional: producer, router, dan consumer yang scale, gagal, dan deploy secara independen. Kalau pipeline pemrosesan gambar Anda kena lonjakan trafik 100x sementara service billing tidak, menaruh queue di antara keduanya jelas benar. Pertanyaannya tidak pernah apakah event itu berfungsi. Pertanyaannya adalah apakah masalah yang diselesaikannya memang masalah yang Anda hadapi hari ini.

## Empat Biaya yang Tidak Pernah Masuk Slide Presentasi

Setiap pola arsitektur adalah pertukaran. Inilah yang Anda bayar untuk event, dan Anda membayarnya sejak hari pertama, bukan saat scale.

## Debugging jadi arkeologi

Panggilan synchronous memberi Anda stack trace. Event memberi Anda correlation ID, itu pun kalau Anda ingat mempropagasikannya, tersebar di tiga service dan satu broker. Median time-to-diagnosis insiden production Anda naik sejak hari pertama go async, dan tidak pernah benar-benar turun lagi.

## Eventual consistency bocor ke UX

Pengguna tidak berpikir dalam eventual consistency. Mereka klik approve, refresh halaman, dan berharap dunia sudah berubah. Setiap batas async yang Anda tambahkan adalah tempat UI bisa menampilkan data basi, dan Anda akan menghabiskan waktu engineering sungguhan menutupinya dengan optimistic update dan polling.

## Penanganan kegagalan berlipat ganda

Kegagalan synchronous adalah satu jalur kode: panggilan gagal, tampilkan error. Kegagalan async adalah kebun binatang: redelivery, pengiriman tidak berurutan, pengiriman duplikat, poison message, dead-letter queue, consumer yang berhenti diam-diam. Masing-masing butuh desain, kode, dan monitoring.

## Beban operasional membengkak

Broker adalah satu lagi stateful service yang harus di-deploy, di-patch, dipantau, dan di-backup. Untuk tim kecil dengan budget VPS, Kafka atau bahkan managed queue adalah pajak perhatian permanen yang tidak dikenakan oleh sebuah tabel Postgres.

## Lima Tanda Event Akan Memperburuk Sistem Anda

Dari pengalaman saya membangun sistem NestJS dan PostgreSQL untuk SMB Indonesia, ini bendera merah bahwa desain event-driven justru akan mengurangi nilai:

Producer dan consumer dipegang tim yang sama dan deploy bersamaan. Anda membayar pajak decoupling penuh untuk men-decouple nol hal. Function call atau transaksi database sudah cukup.

Alur bisnis butuh jawaban langsung dan otoritatif. Reservasi stok, otorisasi pembayaran, booking kursi: kalau pengguna harus tahu hasilnya sebelum lanjut, event di tengah hanya menambah latency dan mode kegagalan.

Anda butuh urutan ketat lintas entitas. Broker umumnya hanya menjamin urutan per partition atau per queue. Kalau logika domain Anda rusak saat pesan B tiba sebelum pesan A, Anda akan berakhir membangun ulang lapisan serialisasi yang sebenarnya sudah diberikan database secara gratis.

Seluruh beban tulis Anda muat nyaman di satu instance PostgreSQL. Kebanyakan sistem SMB puncaknya di bawah beberapa ratus write per detik. Postgres menangani itu di mesin 4 vCPU dengan sisa tenaga. Skalabilitas yang tidak Anda butuhkan bukan aset, itu inventori.

Tidak ada anggota tim yang pernah mengoperasikan broker di production. Insiden poison message pertama jam 2 pagi bukan waktu yang tepat untuk belajar cara kerja dead-letter queue.

Versi termahal dari kesalahan ini adalah event sourcing sebagai system of record. Kalau Anda mengadopsinya tanpa kebutuhan keras seperti mandat audit penuh lewat replay, Anda mendaftar untuk evolusi skema atas sejarah yang immutable. Membalikkan keputusan itu nanti berarti proyek migrasi data, bukan refactor.

## Panggilan Synchronous vs Event: Perbandingan Jujur

Ini tabel yang saya gambar di whiteboard saat tim sedang memutuskan. Tidak ada kolom yang lebih baik; keduanya tagihan yang berbeda.

Dimensi

Synchronous (REST/transaksi)

Event-driven (broker)

Konsistensi

Langsung, transaksional di tempat yang penting

Eventual; UX dan reporting harus mentolerir jeda

Debugging

Satu stack trace, satu log stream

Correlation ID lintas service, dashboard broker, tooling replay

Mode kegagalan

Panggilan gagal, pemanggil memutuskan; timeout berantai saat beban tinggi

Retry, duplikat, urutan, poison message, consumer mati diam-diam

Coupling antar tim

Pemanggil harus tahu API dan uptime yang dipanggil

Tim rilis independen; kontrak pindah ke skema event

Biaya ops untuk tim kecil

Sebatas biaya aplikasi yang sudah ada

Hosting broker, monitoring, upgrade, plus pengetahuan on-call

## Contoh Nyata: Alur Approval yang Tidak Butuh Kafka

Di salah satu proyek ERP, approval purchase order awalnya menerbitkan event POApproved yang diambil consumer inventory untuk mereservasi stok. Di atas kertas, decoupling yang indah. Praktiknya, kedua service dipelihara tiga engineer yang sama dan di-deploy dari monorepo yang sama. Saat payload cacat membuat consumer crash, retry menunda reservasi stok 40 detik sementara staf procurement menatap stok bebas yang seharusnya sudah terkunci.

Kami mengganti event itu dengan satu transaksi database yang meng-update purchase order dan reservasi stok sekaligus. Diagram di bawah adalah kondisi sebelum dan sesudah. Satu kelas insiden terhapus, latency hilang, dan kode menyusut sepertiga. Kami mempertahankan event di tempat yang memang layak: notifikasi ke warehouse reporting dan pengiriman email, di mana jeda 40 detik tidak merugikan apa pun.

## Checklist Keputusan yang Benar-Benar Saya Pakai

Sebelum queue atau topic mana pun masuk desain saya, ia harus lolos lima pertanyaan ini, berurutan:

Apakah ada ketimpangan latency atau availability yang nyata antara producer dan consumer, seperti upload yang melonjak memberi makan pemrosesan yang lambat? Kalau tidak, berhenti di sini dan pakai panggilan atau transaksi.

Bisakah hasil bisnis mentolerir jeda bermenit-menit dan urutan yang sesekali acak? Kalau tidak, langkah itu tetap synchronous semau apa pun diagram topic-nya terlihat elegan.

Apakah producer dan consumer dimiliki tim berbeda dengan ritme deploy berbeda? Men-decouple tim adalah alasan sah terkuat untuk event. Men-decouple kode Anda dari dirinya sendiri bukan.

Apakah consumer idempotent sudah didesain sebelum pesan pertama mengalir? At-least-once delivery berarti duplikat adalah kepastian, bukan edge case.

Apakah ada budget operasional: monitoring consumer lag, strategi dead-letter, dan orang yang bisa menguras queue dalam tekanan? Kalau jawabannya jujur tidak, tunda polanya, bukan fiturnya.

> Mulailah dengan transactional outbox di atas PostgreSQL sebelum meraih broker. Tabel jobs plus worker polling memberi Anda pemrosesan async, retry, dan tetap hanya satu database yang dioperasikan. Anda bisa naik kelas ke BullMQ atau Kafka nanti dengan mengganti transport, karena pola outbox sudah memaksa Anda mendesain handler yang idempotent.

## Di Mana Event Benar-Benar Menang

Supaya tulisan ini bukan serangan sepihak: saya menjalankan alur event-driven dengan senang hati di production hari ini, dan mereka layak dipertahankan di tiga tempat.

Fan-out side effect: satu fakta bisnis, banyak reaksi independen. Invoice dibayar; accounting, email, analytics, dan portal pelanggan semuanya peduli, dan tidak ada yang boleh memblokir yang lain.

Load leveling: producer yang bursty memberi makan consumer yang dibatasi rate, seperti bulk import yang menetes ke API pihak ketiga yang hanya mengizinkan lima request per detik.

Integrasi lintas sistem: ketika consumer adalah sistem perusahaan lain atau ERP legacy yang tidak bisa Anda panggil secara synchronous, event log adalah kontrak terbersih yang bisa Anda tawarkan.

## Membosankan Dulu, Event Saat Sudah Layak

Event-driven architecture adalah alat untuk mengelola kompleksitas organisasi dan profil beban, bukan lencana kedewasaan. Sistem yang paling saya banggakan dimulai sebagai monolith dengan satu database PostgreSQL, menambah transactional outbox saat pekerjaan async muncul, dan baru memperkenalkan broker sungguhan ketika kebutuhan fan-out atau load-leveling konkret datang membawa angka.

Intinya: setiap batas async yang Anda tambahkan harus membayar sewa berupa masalah decoupling atau scaling nyata yang ia selesaikan. Kalau tidak bisa, biarkan panggilan tetap synchronous, pertahankan transaksinya, dan nikmati kemampuan membaca perilaku sistem Anda dari satu stack trace. Diri Anda yang jam 2 pagi akan berterima kasih.

## Sumber dan bacaan lanjutan

- Martin Fowler — What do you mean by Event-Driven?
- AWS — What is an Event-Driven Architecture?

## Source links

- https://martinfowler.com/articles/201701-event-driven.html
- https://aws.amazon.com/event-driven-architecture/

## FAQ

### Kapan sebaiknya Anda sama sekali menghindari event-driven architecture?

Hindari event ketika producer dan consumer dipegang tim yang sama dan deploy bersamaan, ketika alur bisnis butuh jawaban langsung dan otoritatif seperti reservasi stok atau otorisasi pembayaran, atau ketika seluruh beban tulis Anda muat nyaman di satu instance PostgreSQL. Kalau sistem Anda muat di satu database dan tim Anda muat di satu ruangan, panggilan synchronous atau transaksi database hampir selalu lebih tepat.

### Apa saja biaya operasional tersembunyi dari mengadopsi message broker?

Broker adalah satu lagi stateful service yang harus di-deploy, di-patch, dipantau, dan di-backup. Debugging bergeser dari membaca satu stack trace menjadi mengejar correlation ID lintas beberapa service dan dashboard broker. Anda juga mewarisi kebun binatang mode kegagalan async — retry, pengiriman duplikat, pesan tidak berurutan, poison message, dan dead-letter queue — yang masing-masing butuh desain, kode, dan monitoring sejak hari pertama.

### Bagaimana mengganti event Kafka dengan transaksi database bisa memperbaiki sistem ERP nyata?

Di salah satu proyek ERP, event POApproved memicu consumer inventory untuk mereservasi stok, namun karena kedua service dipelihara tiga engineer yang sama dari monorepo yang sama, payload cacat bisa menunda reservasi stok hingga 40 detik melalui retry. Mengganti event itu dengan satu transaksi database menghapus kelas insiden tersebut sepenuhnya, menghilangkan latency, dan menyusutkan kode sepertiga. Event dipertahankan hanya di tempat yang memang mentolerir jeda — notifikasi ke warehouse reporting dan pengiriman email.

### Apa langkah awal yang direkomendasikan sebelum menggunakan message broker penuh?

Mulailah dengan pola transactional outbox di atas PostgreSQL: tabel jobs ditambah polling worker memberi Anda pemrosesan async dan retry dengan hanya satu database yang harus dioperasikan. Anda bisa beralih ke BullMQ atau Kafka nanti dengan mengganti lapisan transport, karena pola outbox sudah memaksa Anda mendesain idempotent handler — bagian tersulit dari sistem event-driven mana pun.

### Kapan event benar-benar layak dipakai dalam sebuah sistem?

Event dibenarkan ketika ada ketimpangan latency atau availability yang nyata antara producer dan consumer seperti upload yang melonjak memberi makan pemrosesan yang lambat, ketika producer dan consumer dimiliki tim berbeda dengan ritme deploy berbeda, dan ketika hasil bisnis bisa mentolerir jeda bermenit-menit dan urutan yang sesekali acak. Notifikasi ke warehouse reporting dan pengiriman email adalah contoh klasik di mana jeda 40 detik tidak merugikan apa pun dan decoupling benar-benar terbayar.

---

- HTML version: https://www.matthewswong.com/id/blog/event-driven-architecture-when-not-to/
- English: https://www.matthewswong.com/en/blog/event-driven-architecture-when-not-to/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
