Event-Based Architecture — Realtime

Event-Based Architecture Dalam real-time systems, everything is an event. User mengetik → event. Pesan terkirim → event. User join → event. Arsitektur…

Event-based architecture adalah cara menyusun aplikasi supaya bagian-bagiannya saling memberi kabar lewat event: catatan bahwa sesuatu sudah terjadi, dikirim ke siapa pun yang mendengarkan, tanpa pengirimnya tahu siapa saja pendengarnya. Bayangin tiga cara bicara di Laundry Bu Rini. Bu Rini menyuruh "Mas, setrika nomor 12!": itu perintah, ditujukan ke satu orang, dan bisa ditolak. Mas Eko berseru "Nomor 12 sudah disetrika!": itu event, kabar tentang hal yang sudah terjadi, dan siapa pun yang dengar boleh bereaksi (Bu Rini memperbarui papan, kasir mengabari Pak Darto). Papan di meja depan yang menulis status semua order saat ini: itu state, hasil dari semua kabar tadi.

🧪 Tebakan cepat: HP Pak Darto menerima event order:status "Siap diambil" untuk order #12 dua kali, karena server mengirim ulang. Handler-nya cuma mengganti status lalu menampilkan notifikasi. Apa yang dilihat Pak Darto? Jawabannya ada di bagian "Kabar bisa hilang, bisa datang dua kali".

Event, perintah, dan state

Di laundry Di kode
Perintah "Ubah #12 jadi Disetrika" tablet: socket.emit('order:set-status', { id: 12, status: 'Disetrika' }, ack). Server memeriksa, boleh menolak.
Event "#12 sudah Disetrika" server ke semua layar: order:status dengan { id: 12, status: 'Disetrika', ... }
State papan berisi 4 order GET /papan: #12 Dicuci, #13 Disetrika, #14 Siap diambil, #15 Antre

Alurnya selalu sama: perintah masuk, server memeriksa dan menyimpan, baru event dikirim. Layar yang baru dibuka mulai dari state (GET /papan), lalu menerapkan event satu per satu ke state miliknya sendiri.

Emit dan listen

Di server realtime (Node.js), pola ini sudah ada bawaan: EventEmitter.

import { EventEmitter } from 'node:events'

const bus = new EventEmitter()

// tiga pendengar, tiga urusan, gak saling kenal
bus.on('order:status', (e) => io.to('papan').emit('order:status', e))         // papan dan tablet
bus.on('order:status', (e) => io.to(`order:${e.id}`).emit('order:status', e)) // HP pelanggan order itu
bus.on('order:status', (e) => console.log('log', e.id, e.status))

// di handler perintah dari tablet, setelah status tersimpan:
bus.emit('order:status', { id: 12, status: 'Disetrika', seq: 3, at: '2026-10-11T09:12:40+07:00', by: 'eko' })

Merancang nama dan isi event

Nama: benda:kejadian, huruf kecil, konsisten di seluruh aplikasi: order:status, chat:message, presence:online. Isinya:

{ "id": 12, "status": "Siap diambil", "seq": 4, "at": "2026-10-11T09:15:02+07:00", "by": "eko" }

Jangan kirim seluruh papan di tiap event, dan jangan kirim data yang gak boleh dilihat semua layar (nomor HP pelanggan, misalnya).

Mengubah isi event tanpa merusak layar lama

Tablet Mas Eko bisa menjalankan JavaScript versi lama berhari-hari (tab gak di-refresh, aplikasi PWA di cache). Jadi:

Kabar bisa hilang, bisa datang dua kali

Kabar masuk ke HP Pak Darto Handler polos Handler idempoten
seq 3, Disetrika Disetrika Disetrika
seq 4, Siap diambil Siap diambil, notifikasi ke-1 Siap diambil, notifikasi ke-1
seq 4, Siap diambil (dobel) notifikasi ke-2 diabaikan

Itu jawaban tebakannya: dengan handler polos, notifikasi "Cucian #12 siap diambil" muncul dua kali. Statusnya sendiri benar, karena isinya nilai akhir. Yang dobel adalah efek sampingnya. Handler idempoten mengingat seq terakhir tiap order:

const terakhir = new Map() // id order -> seq terakhir yang sudah diterapkan

socket.on('order:status', (e) => {
  if (e.seq <= (terakhir.get(e.id) ?? 0)) return // dobel atau basi: abaikan
  terakhir.set(e.id, e.seq)
  papan[e.id] = e.status
  if (e.status === 'Siap diambil') tampilkanNotif(`Cucian #${e.id} siap diambil`)
})

Urutan dijaga per order

Socket.IO menjaga urutan di dalam satu koneksi. Tapi kabar bisa datang dari dua jalan. Setelah tersambung lagi, HP Pak Darto meminta GET /pesanan/12 (isinya seq 3, Disetrika), dan sebelum jawabannya tiba, event seq 4 "Siap diambil" datang lewat socket:

Datang seq Handler polos Dengan cek seq
socket: Siap diambil 4 Siap diambil Siap diambil
HTTP: Disetrika 3 Disetrika (mundur!) diabaikan

Urutan cuma perlu benar per order: kabar #13 boleh menyalip kabar #12. Karena itu seq dihitung per order, dan handler yang sama menangani dobel sekaligus basi.

⚠️ Jebakan yang sering ketemu

TL;DR: Event adalah kabar tentang hal yang sudah terjadi (order:status), perintah adalah permintaan yang boleh ditolak (order:set-status), state adalah hasil semuanya (GET /papan). Bagian-bagian aplikasi berkomunikasi lewat emit dan on, bukan saling memanggil langsung; nama di socket.on("message", ...) adalah nama event. Isi event: id, nilai akhir status, seq per order, at dan by dari server. Socket.IO bawaannya at-most-once (event saat putus hilang); kirim ulang membuatnya at-least-once (bisa dobel). Jadi buat handler idempoten: abaikan event yang seq-nya gak lebih besar dari yang terakhir diterapkan, dan menambah field lebih aman daripada mengganti.

Yang akan kamu pelajari