Jawaban singkatnya: kompleksitas event-driven layak dibayar ketika alur bisnis Anda mulai didominasi proses asinkron, banyak efek samping lintas modul, throughput meningkat, dan model request-response sinkron mulai menciptakan coupling, timeout, atau bottleneck tim. Jika aplikasi masih sederhana, alur utamanya transaksi langsung, dan konsistensi instan lebih penting daripada skalabilitas alur kerja, maka arsitektur CRUD sinkron biasanya tetap pilihan yang lebih efisien.
Masalahnya bukan apakah event-driven lebih modern, tetapi apakah biaya tambahannya masuk akal: broker, retry, idempotensi, observabilitas, versioning event, debugging lintas layanan, sampai beban on-call. Banyak tim terlalu cepat pindah dari CRUD ke event-driven, lalu justru kehilangan produktivitas karena kompleksitas operasional naik lebih cepat daripada manfaat bisnisnya.
CRUD sinkron vs event-driven: perbedaan yang relevan untuk keputusan
Untuk konteks artikel ini, CRUD sinkron berbasis request-response berarti alur utama aplikasi diproses langsung dalam satu permintaan API atau web request. Misalnya:
- Client memanggil endpoint
POST /orders. - Service menulis ke database.
- Service memanggil modul pembayaran, notifikasi, atau inventori secara langsung.
- Respons sukses atau gagal dikembalikan dalam jalur request yang sama.
Pendekatan ini sederhana untuk dipahami, diuji, dan di-debug. Jika ada error, stack trace biasanya jelas dan korelasi antar langkah mudah ditelusuri.
Sebaliknya, pada arsitektur event-driven, service yang menerima request tidak harus menjalankan semua efek samping saat itu juga. Ia dapat:
- menyimpan data inti,
- menerbitkan event seperti
OrderCreated, - membiarkan consumer lain memproses pembayaran, email, sinkronisasi gudang, analytics, atau integrasi eksternal secara terpisah.
Keuntungan utamanya adalah decoupling dan asynchronous workflow. Harga yang dibayar adalah hilangnya kesederhanaan alur sinkron dan meningkatnya kebutuhan akan disiplin teknik.
Kapan CRUD sinkron masih pilihan terbaik
Jangan pindah ke event-driven hanya karena sistem mulai tumbuh. Banyak aplikasi tetap sehat cukup lama dengan arsitektur CRUD yang rapi, boundary modul yang jelas, dan penggunaan queue terbatas untuk tugas non-kritis.
Sinyal bahwa CRUD masih cukup
- Alur bisnis utama harus selesai saat itu juga. Misalnya validasi stok, simpan transaksi, dan respons ke pengguna perlu konsisten dalam satu langkah.
- Jumlah efek samping masih sedikit. Contoh: setelah user mendaftar, hanya perlu kirim email verifikasi dan log audit.
- Tim kecil dan generalist. Menambah broker, consumer, tracing, dan retry policy bisa lebih mahal daripada manfaatnya.
- Kebutuhan debugging cepat lebih penting. Sistem internal backoffice sering lebih cocok dengan jalur sinkron yang mudah ditelusuri.
- Data consistency kuat lebih dominan daripada throughput. Misalnya sistem administrasi, panel internal, master data, atau aplikasi B2B dengan volume moderat.
Masalah yang sering salah didiagnosis sebagai kebutuhan event-driven
Beberapa bottleneck sebenarnya bisa diselesaikan tanpa mengubah arsitektur inti:
- Endpoint lambat karena query database buruk, bukan karena request-response salah desain.
- Integrasi eksternal lambat bisa dipindah ke background job tanpa mengadopsi event-driven penuh.
- Coupling antar modul terjadi karena boundary domain kabur, bukan karena Anda belum punya broker.
- Timeout meningkat karena operasi berat belum dipisah dari request utama.
Aturan praktis: jika masalah utama Anda masih bisa diselesaikan dengan caching, indexing, queue untuk tugas background, dan refactor boundary service, maka belum tentu perlu event-driven penuh.
Kapan transisi ke event-driven mulai layak
Transisi biasanya masuk akal ketika request-response sinkron mulai menjadi hambatan struktural, bukan sekadar masalah implementasi.
1. Efek samping lintas modul makin banyak
Contoh saat order dibuat, sistem harus:
- mengurangi stok,
- memicu pembayaran,
- mengirim notifikasi,
- mencatat audit trail,
- mengirim data ke ERP,
- memperbarui materialized view atau search index.
Jika semua ini dilakukan sinkron dalam satu request, coupling naik cepat. Kegagalan satu integrasi bisa ikut menggagalkan alur utama, atau memaksa banyak fallback rumit di jalur request.
2. Throughput meningkat dan request mulai menumpuk
Ketika volume event bisnis naik, memindahkan pekerjaan non-kritis dari request utama ke consumer asinkron bisa mengurangi latency dan memperbaiki kapasitas sistem. Ini relevan terutama untuk:
- checkout volume tinggi,
- webhook processing,
- sinkronisasi ke banyak sistem eksternal,
- pipeline notifikasi atau analytics.
Namun, throughput tinggi saja belum cukup. Pastikan bottleneck benar-benar ada di orkestrasi sinkron, bukan di database atau resource lain.
3. Workflow memang asinkron secara alami
Beberapa domain memang lebih cocok diproses bertahap:
- pemrosesan file atau media,
- fraud check,
- fulfillment order,
- rebuild projection/read model,
- integrasi vendor yang tidak selalu responsif.
Jika pengguna tidak perlu menunggu semua langkah selesai, event-driven memberi model yang lebih natural.
4. Banyak tim bekerja pada domain berbeda
Saat satu perubahan di modul order selalu memaksa perubahan sinkron di billing, notification, loyalty, dan reporting, coupling organisasi biasanya mulai muncul. Event yang stabil dapat menjadi kontrak antar modul atau antar tim, selama schema dan versioning dikelola dengan baik.
5. Kebutuhan replay dan audit makin penting
Pada beberapa kasus, menyimpan jejak event membantu untuk:
- membangun ulang projection,
- mengoreksi consumer yang rusak,
- menjalankan ulang integrasi,
- melacak urutan perubahan bisnis.
Ini bukan berarti harus memakai event sourcing. Tetapi event sebagai mekanisme integrasi bisa memberi manfaat operasional nyata.
Trade-off teknis yang sering diremehkan
Debugging lebih sulit
Di CRUD sinkron, alur satu request relatif linear. Pada event-driven, satu aksi pengguna bisa memicu banyak event, consumer, retry, dan dead-letter queue. Error tidak selalu muncul saat request diterima, melainkan beberapa detik atau menit kemudian di consumer berbeda.
Implikasinya:
- Anda butuh correlation ID atau trace ID lintas service.
- Log harus terstruktur dan konsisten.
- Perlu cara melihat status end-to-end, bukan hanya log per service.
Tanpa ini, debugging berubah menjadi pencarian manual di banyak tempat.
Konsistensi data menjadi eventual, bukan instan
Pada request-response sinkron, lebih mudah menjamin bahwa setelah respons sukses, semua langkah penting sudah terjadi. Pada event-driven, Anda sering harus menerima eventual consistency: data di service A sudah berubah, tetapi projection atau service B baru menyusul beberapa saat kemudian.
Konsekuensinya:
- UI dan API perlu mengantisipasi data yang belum sepenuhnya sinkron.
- Logika bisnis tidak boleh diam-diam mengasumsikan konsistensi instan.
- Tim harus jelas membedakan mana proses yang boleh tertunda dan mana yang wajib atomik.
Retry memunculkan duplikasi
Begitu ada queue atau broker, Anda harus berasumsi bahwa pesan dapat diproses lebih dari sekali. Penyebabnya bisa timeout, consumer crash setelah menulis sebagian data, atau ack yang gagal terkirim.
Artinya, consumer harus dirancang idempotent.
Contoh sederhana:
// Pseudocode consumer yang idempotent
function handleOrderCreated(event) {
if (processedEventExists(event.id)) {
return;
}
beginTransaction();
reserveInventory(event.orderId, event.items);
markProcessed(event.id);
commit();
}Prinsipnya bukan hanya "cek lalu proses", tetapi memastikan pencatatan event yang sudah diproses dilakukan dengan aman terhadap race condition. Biasanya ini memerlukan:
- unique constraint pada
event_id, - transaksi database yang tepat,
- operasi bisnis yang aman untuk dipanggil ulang.
Schema dan event versioning harus dikelola
Begitu event menjadi kontrak antar modul, perubahan payload tidak bisa sembarangan. Menambah field biasanya aman jika consumer toleran terhadap field baru. Menghapus atau mengubah makna field jauh lebih berisiko.
Praktik yang umumnya membantu:
- anggap event sebagai kontrak publik, bukan detail internal yang bebas berubah,
- gunakan field yang eksplisit dan stabil,
- hindari payload yang terlalu kaya dan meniru seluruh tabel database,
- sediakan versi event saat perubahan breaking tidak bisa dihindari.
Nama event seperti user.updated sering terlalu generik jika struktur dan makna datanya terus berubah. Lebih baik event merepresentasikan fakta bisnis yang jelas.
Observabilitas bukan opsional
Pada sistem sinkron kecil, log aplikasi dan dashboard database kadang sudah cukup. Pada event-driven, itu jarang memadai. Minimal Anda perlu:
- metric publish/consume rate,
- queue lag atau backlog,
- retry count dan dead-letter volume,
- distributed tracing atau correlation ID,
- alerting untuk consumer yang macet atau lambat.
Tanpa observabilitas, event-driven cepat berubah dari arsitektur yang fleksibel menjadi sistem yang sulit dipercaya.
Biaya operasional yang nyata
Keputusan arsitektur bukan hanya soal coding. Event-driven menambah biaya operasional yang sering baru terasa setelah produksi.
Infrastruktur tambahan
- broker atau queue,
- consumer worker dan autoscaling-nya,
- penyimpanan dead-letter,
- dashboard monitoring dan tracing.
Walau layanan terkelola bisa mengurangi beban, Anda tetap harus memahami failure mode-nya.
Beban on-call meningkat
Pada CRUD sinkron, gangguan sering terlihat langsung lewat error rate API. Pada event-driven, sistem bisa tampak sehat dari sisi API tetapi backlog queue diam-diam menumpuk. Incident menjadi lebih halus:
- event masuk tetapi tidak diproses,
- consumer retry tanpa henti,
- pesan masuk dead-letter karena schema berubah,
- proyeksi tertinggal jauh dari sumber data.
On-call perlu runbook yang spesifik: kapan replay aman, kapan stop consumer, kapan purge queue dilarang, dan bagaimana memverifikasi efek samping parsial.
Tooling dan local development lebih berat
Menjalankan API lokal jauh lebih mudah daripada menjalankan API + broker + beberapa consumer + observability stack. Ini berdampak langsung pada produktivitas developer, terutama pada tim kecil. Jika waktu setup dan reproduksi bug terlalu panjang, manfaat decoupling bisa terkikis.
Pola transisi yang lebih aman: hybrid lebih sering masuk akal
Banyak sistem tidak perlu melompat dari CRUD murni ke event-driven penuh. Pendekatan hybrid sering menjadi kompromi terbaik:
- alur inti tetap sinkron,
- efek samping non-kritis dipindah ke asynchronous processing,
- event digunakan selektif untuk integrasi atau projection.
Contoh: saat order dibuat, API tetap menyimpan order dan mengembalikan respons cepat. Lalu event internal diterbitkan untuk email, analytics, sinkronisasi ERP, atau pembaruan read model.
Ini mengurangi risiko karena Anda tidak memaksa semua dependensi menjadi eventual consistency sekaligus.
Contoh alur hybrid yang umum
Client
-> POST /orders
-> Order Service
-> simpan order ke DB
-> commit transaksi
-> publish OrderCreated
<- 201 Created
Consumers:
- Notification Consumer -> kirim email
- Analytics Consumer -> update dashboard
- ERP Consumer -> sinkronisasi eksternalJika publishing event dilakukan, perhatikan masalah klasik: bagaimana memastikan data tersimpan dan event benar-benar terkirim tanpa kehilangan salah satunya?
Solusi yang umum adalah pola transactional outbox:
- tulis perubahan bisnis dan record outbox dalam transaksi database yang sama,
- proses terpisah membaca outbox lalu mem-publish ke broker,
- setelah sukses, tandai record outbox sudah dikirim.
Ini bukan satu-satunya pola, tetapi sangat berguna untuk menghindari kondisi "data sudah commit, event tidak terkirim" atau sebaliknya.
// Pseudocode transactional outbox
beginTransaction();
insertOrder(order);
insertOutbox({
type: 'OrderCreated',
aggregate_id: order.id,
payload: jsonEncode(orderEventPayload)
});
commit();Pola ini menambah komponen, tetapi biasanya lebih aman daripada publish event langsung sebelum atau sesudah commit tanpa mekanisme pemulihan.
Matriks keputusan: tetap di CRUD, hybrid, atau event-driven?
| Kondisi | CRUD Sinkron | Hybrid | Event-Driven Lebih Kuat |
|---|---|---|---|
| Efek samping setelah aksi utama | Sedikit dan sederhana | Mulai banyak, sebagian bisa async | Banyak, lintas domain, saling independen |
| Kebutuhan konsistensi instan | Tinggi | Inti tinggi, turunan boleh eventual | Banyak proses boleh eventual consistency |
| Throughput | Moderat | Naik di area tertentu | Tinggi dan memicu bottleneck sinkron |
| Coupling antar modul | Masih rendah | Mulai terasa | Tinggi dan menghambat perubahan |
| Integrasi eksternal | Sedikit | Beberapa integrasi lambat/tidak stabil | Banyak integrasi dan workflow asinkron |
| Kematangan observabilitas | Dasar sudah cukup | Perlu meningkat | Wajib kuat sebelum adopsi luas |
| Kapasitas tim operasional | Terbatas | Sedang | Siap menangani broker, lag, retry, replay |
| Produktivitas developer | Prioritas utama adalah kesederhanaan | Ingin pisahkan beban tertentu | Siap membayar kompleksitas demi fleksibilitas |
Checklist keputusan cepat
- Apakah request utama sekarang sering timeout karena melakukan terlalu banyak pekerjaan?
- Apakah sebagian besar pekerjaan itu sebenarnya tidak perlu selesai sebelum respons dikirim?
- Apakah modul lain perlu bereaksi pada fakta bisnis yang sama tanpa coupling langsung?
- Apakah tim sudah siap mengelola retry, idempotensi, DLQ, dan tracing?
- Apakah ada orang yang akan memelihara broker, consumer, alerting, dan runbook?
Jika jawaban untuk 1-3 adalah ya, tetapi 4-5 masih tidak, biasanya hybrid adalah langkah yang lebih rasional daripada event-driven penuh.
Contoh skenario nyata
Tetap di CRUD
Aplikasi admin internal untuk master data produk. Operasi utamanya create/update/delete, volume tidak ekstrem, dan setiap perubahan harus langsung terlihat konsisten di UI. Menambah event broker untuk semua perubahan justru memperumit debugging dan deployment.
Hybrid
Aplikasi e-commerce menengah. Checkout perlu menyimpan order dan memberi respons cepat. Tetapi email, analytics, sinkronisasi partner logistik, dan pembaruan dashboard tidak perlu sinkron. Alur inti tetap CRUD sinkron, sementara efek samping berjalan lewat queue/event.
Event-driven masuk akal
Platform dengan banyak domain dan integrasi. Misalnya order memicu fulfillment, pembayaran, loyalty, anti-fraud, invoice, notifikasi, dan pelaporan ke beberapa sistem. Banyak tim mengerjakan area berbeda, throughput tinggi, dan dependensi sinkron mulai memperlambat perubahan. Dalam kondisi ini, event sebagai kontrak integrasi bisa memberi manfaat yang sebanding dengan kompleksitasnya.
Kesalahan implementasi yang umum
- Mengubah semua hal menjadi event terlalu cepat. Tidak semua interaksi perlu asynchronous.
- Tidak membedakan command dan event. Event adalah fakta yang sudah terjadi, bukan instruksi samar yang memindahkan coupling ke level lain.
- Tidak mendesain idempotensi sejak awal. Ini hampir selalu berujung duplikasi data atau efek samping ganda.
- Payload event terlalu gemuk. Consumer menjadi tergantung pada struktur internal producer.
- Tidak punya DLQ dan replay strategy. Begitu ada pesan gagal, tim bingung harus berbuat apa.
- Mengabaikan observabilitas. Sistem tampak bekerja sampai suatu hari backlog menumpuk tanpa disadari.
Panduan praktis sebelum memutuskan transisi
Pilih tetap di CRUD jika
- alur utama sederhana dan perlu konsistensi langsung,
- jumlah modul terbatas,
- tim kecil lebih diuntungkan oleh arsitektur yang mudah dipahami,
- problem saat ini lebih banyak berasal dari query, deployment, atau desain modul yang belum rapi.
Pilih hybrid jika
- inti transaksi tetap sinkron,
- ada banyak efek samping yang tidak perlu memblok request,
- Anda ingin mulai mendekopel integrasi tanpa memindahkan seluruh model sistem,
- tim masih membangun kapabilitas monitoring dan operasi asynchronous workload.
Pilih event-driven lebih luas jika
- workflow bisnis benar-benar asinkron dan lintas domain,
- coupling sinkron sudah menjadi hambatan utama perubahan,
- throughput dan kebutuhan integrasi membuat request-response terlalu rapuh,
- tim siap membayar biaya operasional dan disiplin engineering yang lebih tinggi.
Penutup
CRUD ke event-driven bukan perjalanan yang wajib, dan bukan pula tanda kedewasaan teknis dengan sendirinya. Kompleksitasnya layak dibayar hanya ketika ia menyelesaikan masalah struktural yang nyata: throughput, coupling, workflow asinkron, atau bottleneck organisasi. Jika tidak, Anda hanya menukar sistem yang sederhana tetapi jelas dengan sistem yang fleksibel tetapi lebih mahal di-debug, dioperasikan, dan dipelihara.
Dalam banyak kasus, keputusan terbaik bukan memilih salah satu secara absolut, melainkan memulai dari CRUD yang bersih, menambahkan queue untuk pekerjaan non-kritis, lalu bergerak ke model hybrid secara sengaja. Jika suatu hari event-driven penuh benar-benar diperlukan, fondasinya sudah lebih siap: boundary domain jelas, observabilitas matang, dan tim memahami konsekuensinya.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!