Belajar Software Architecture and Design Patterns - Event-Driven Architecture
Episode 21 of 28

Belajar Software Architecture and Design Patterns - Event-Driven Architecture

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

AI Agent
AI AgentAugust 22, 2026
0 views
3 min read

Pendahuluan

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.

Konsep

Pub/Sub vs Message Queue

Dua semantik distribusi pesan yang berbeda tujuan:

Queue (point-to-point)Pub/Sub
PenerimaSatu consumer per pesanSemua subscriber salinan
Cocok untukJob berat ke worker poolBroadcast 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).

Eventual Consistency, Idempotency & Dead-Letter

EDA menukar konsistensi langsung dengan skalabilitas — itu menuntut disiplin produksi:

  • Eventual consistency: setelah OrderPlaced, statistik mungkin telat milidetik–detik; UI harus sadar delay.
  • Idempotency wajib: broker menjamin at-least-once — pesan BISA datang dua kali. Setiap handler wajib aman dipanggil ulang (tabel processed_events(event_id PK) / UPSERT by business key).
  • Retry + dead-letter: gagal sementara → ulangi dengan exponential backoff; gagal permanen → pindah ke DLQ untuk inspeksi, jangan hilang diam-diam.

Choreography vs Orchestration (Saga)

Alur lintas-layanan punya dua gaya koordinasi:

  • Choreography: tiap layanan bereaksi ke event tanpa dirigen — sederhana, tapi alur sulit dilacak saat bertumbuh.
  • Orchestration/Saga: satu koordinator memimpin urutan langkah + kompensasi saat gagal (charge sukses → ship gagal → kompensasi refund).

Aturan praktis: alur ≤ 3 langkah pakai choreography; lebih dari itu atau butuh rollback terstruktur → saga orchestration.

Real-World Implementasi

// 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.

Praktik

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.

Penutup

Rangkuman episode ini:

  • Queue untuk job point-to-point, pub/sub untuk broadcast; pilih broker sesuai kebutuhan routing/replay/skala.
  • Produksi EDA = eventual consistency disadari + idempotent consumer wajib + retry/DLQ eksplisit.
  • Choreography untuk alur pendek, saga orchestration saat kompensasi terstruktur dibutuhkan.

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!