Event-driven architecture: semantik pub/sub versus point-to-point message queue, broker modern dari RabbitMQ Kafka NATS hingga Redis Streams, eventual consistency dengan idempotent consumer wajib karena jaminan at-least-once, retry exponential backoff sampai dead-letter queue, serta pemilihan choreography versus orchestration saga untuk alur lintas layanan — order created memicu pengiriman email receipt dan update statistik via event fanout di ketiga stack: @nestjs/microservices dengan transport RabbitMQ, client NATS plus worker goroutine di Fiber Go, dan Events Listeners Queues Redis dengan dashboard Horizon monitoring di Laravel PHP

Domain events (ep.19) saat ini masih hidup dalam satu proses aplikasi. Event-Driven Architecture (EDA) melepaskan mereka keluar: publisher tidak lagi peduli siapa mendengar — konsumen bisa berupa modul lain, layanan lain, bahkan sistem organisasi lain. Episode ini membahas mekanik yang membuat EDA aman di produksi: broker, idempotency, retry/dead-letter, dan dua filosofi koordinasi alur.
Dua semantik distribusi pesan yang berbeda tujuan:
| Queue (point-to-point) | Pub/Sub | |
|---|---|---|
| Penerima | Satu consumer per pesan | Semua subscriber salinan |
| Cocok untuk | Job berat ke worker pool | Broadcast OrderPlaced ke email + statistik + fraud |
Broker populer 2026 dan karakternya: RabbitMQ (routing fleksibel, exchange), Kafka (log persisten, replayable, throughput tinggi), NATS/JetStream (super ringan, favorit microservices Go), Redis Streams (cukup untuk skala menengah bila Redis sudah ada).
EDA menukar konsistensi langsung dengan skalabilitas — itu menuntut disiplin produksi:
OrderPlaced, statistik mungkin telat milidetik–detik; UI harus sadar delay.processed_events(event_id PK) / UPSERT by business key).Alur lintas-layanan punya dua gaya koordinasi:
charge sukses → ship gagal → kompensasi refund).Aturan praktis: alur ≤ 3 langkah pakai choreography; lebih dari itu atau butuh rollback terstruktur → saga orchestration.
// main.ts - hubungkan transport:
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.RABBITMQ,
options: { urls: [process.env.RABBIT_URL!], queue: 'orders-events',
queueOptions: { durable: true } },
});
// Publisher (modul orders) - emit saat PlaceOrder sukses:
this.eventEmitter.emit('order.placed', new OrderPlacedEvent(order.id));
// Consumer (modul statistik):
@EventPattern('order.placed')
async onOrderPlaced(e: OrderPlacedEvent) {
if (await this.processed.exists(e.eventId)) return; // idempotency guard
await this.stats.increment(e.customerId);
await this.processed.mark(e.eventId);
}Perhatikan perubahan mentalnya: application service (ep.19) tidak lagi memanggil mailer/statistik langsung — ia hanya merekam fakta. Siapa merespons fakta itu adalah keputusan terpisah yang bisa berubah tanpa menyentuh modul orders.
Target outline: order created → kirim email + update statistik via event di ketiga stack.
1. Naikkan broker:
docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3-management
docker run -d -p 4222:4222 nats:2 -js # untuk lab Go
2. Publisher: sukses PlaceOrder -> publish OrderPlaced{id,eventId,customerId}
3. Dua subscriber INDEPENDEN:
a. mailer-consumer : kirim email receipt (simulasikan log)
b. stats-consumer : increment counter customer
4. Uji durability: matikan stats-consumer -> publish -> nyalakan lagi
-> event tetap diproses (queue durable / JetStream durable)
5. Uji idempotency: publish event dengan ID sama dua kali
-> counter naik SEKALI saja
6. Uji dead-letter: paksa handler throw selalu -> amati retry
backoff lalu pesan mendarat di DLQ/failed jobs
7. Commit "ep21: eda fanout order-placed"Note
Latency bukan satu-satunya harga EDA — debugging juga berubah: satu request tidak lagi punya satu stack trace. Investasikan sejak awal pada correlation id yang ikut dalam event agar rantai publish-consume bisa direkonstruksi di log.
Rangkuman episode ini:
Episode 22 menutup trio event dengan pola penyelamat data: Event Sourcing & Outbox Pattern — state sebagai replay events, dual-write problem, dan cara publish aman bersama commit database. Sampai jumpa!