← Back to list

[Bidding #13] Event Sourcing dan CQRS di Golang

Memisahkan write model dan read model untuk membangun sistem realtime yang lebih scalable dan maintainable

Andrian Tri Putra · 2026-06-18 01:01 · 0 claps · 9.5 min read
#golang #redis #distributed-systems #event-driven-architecture #system-design-concepts
Open on Medium ↗
Wiki topics: 🏛️ · Architecture

[Bidding #13] Event Sourcing dan CQRS di Golang

Memisahkan write model dan read model untuk membangun sistem realtime yang lebih scalable dan maintainable

1. Opening

Ketika State Tidak Lagi Menjadi Source of Truth

Di bagian sebelumnya, sistem kita sudah mulai memiliki:

  • distributed worker
  • snapshot recovery
  • partition ownership
  • failover processing

Secara architecture, sistem sudah jauh lebih resilient dibanding awal seri ini.

Namun sekarang muncul pertanyaan baru:

sebenarnya source of truth system kita ada di mana?

Sampai di titik ini:

  • state selalu diupdate langsung
  • memory menjadi representasi current state
  • snapshot membantu recovery
  • stream digunakan untuk replay dan processing

Tetapi perlahan kita mulai melihat sesuatu:

event sebenarnya lebih penting daripada state

Karena:

  • state bisa hilang
  • memory bisa restart
  • snapshot bisa stale

Tetapi:

history event tetap ada

Dan dari history itulah:

  • state bisa dibangun ulang
  • processing bisa direplay
  • projection bisa dipulihkan kembali

Di sinilah konsep:

Event Sourcing

mulai menjadi penting.

Karena dalam event-driven architecture:

event bukan sekadar message, tetapi sumber utama kebenaran system

Artinya:

  • state hanyalah hasil turunan
  • event menjadi canonical history
  • replay menjadi bagian inti architecture

Dan ketika event menjadi source of truth:

write flow

dan:

read flow

mulai bisa dipisahkan.

Di sinilah:

CQRS

mulai masuk ke architecture kita.

Di bagian ini, kita akan mulai membangun:

  • event store sederhana
  • replay-based state rebuild
  • projection flow
  • dan separation antara write dan read processing

agar sistem kita mulai bergerak menuju:

event-native distributed realtime architecture.

2. Kenapa Event Sourcing Penting?

Event Menjadi Sumber Kebenaran

Dalam sistem tradisional:

state dianggap source of truth

Artinya:

  • database menyimpan current state
  • update langsung overwrite data lama
  • history sering hilang

Pendekatan ini memang sederhana.

Tetapi dalam distributed realtime system:

  • node bisa restart
  • memory bisa hilang
  • recovery bisa gagal
  • processing bisa perlu direplay

Dan di titik ini:

current state saja tidak cukup

⚡ Event Menyimpan Seluruh History System

Berbeda dengan state biasa, event menyimpan:

  • apa yang terjadi
  • kapan terjadi
  • siapa yang melakukan
  • urutan perubahan system

Contoh:

andi bid 100
budi bid 200
charlie bid 300

Dari history itulah:

state bisa dibangun ulang

🧠 State Menjadi Hasil Turunan

Dalam event sourcing:

event adalah source of truth

Sedangkan:

state hanyalah projection

Artinya current state sebenarnya hanyalah:

  • hasil replay event
  • hasil processing history
  • representasi sementara system

⚡ Recovery Menjadi Lebih Natural

Karena event tetap tersimpan:

  • state bisa direbuild kapan saja
  • replay menjadi capability utama
  • recovery tidak bergantung pada memory aktif

Flow-nya menjadi:

event
→ replay
→ rebuild state

🔄 Event Sourcing Sangat Cocok Dengan Distributed System

Karena distributed architecture biasanya:

  • asynchronous
  • replay-driven
  • recovery-heavy
  • event-oriented

Dan event sourcing membantu:

  • traceability
  • auditability
  • recovery consistency
  • historical replay

⚡ Event Tidak Hilang Ketika State Hilang

Ini insight penting.

Ketika:

memory reset

state bisa hilang.

Tetapi selama:

event history masih ada

maka:

  • projection bisa dipulihkan
  • state bisa dibangun ulang
  • processing bisa dilanjutkan

🧠 Banyak Platform Modern Menggunakan Event Sebagai Foundation

Konsep seperti ini digunakan di:

  • stream processing platform
  • financial ledger
  • distributed workflow engine
  • realtime analytics system

Karena:

event memberikan history penuh tentang bagaimana system berubah

🎯 Posisi Kita Sekarang

Sekarang sistem kita mulai bergerak dari:

state-centric architecture

menuju:

event-centric architecture

di mana:

  • event menjadi canonical history
  • replay menjadi core capability
  • dan state hanyalah hasil projection dari event stream.

3. Konsep CQRS

Memisahkan Write dan Read Flow

Setelah event mulai menjadi source of truth, muncul perubahan besar dalam architecture:

write flow dan read flow tidak lagi harus berjalan dengan cara yang sama

Di sinilah konsep:

CQRS

mulai masuk.

CQRS adalah singkatan dari:

Command Query Responsibility Segregation

Sederhananya:

  • write bertugas menghasilkan event
  • read bertugas membaca projection state

⚡ Write dan Read Memiliki Kebutuhan Berbeda

Dalam realtime system:

  • write flow fokus pada append event
  • read flow fokus pada query state cepat

Dan keduanya sering memiliki:

  • bottleneck berbeda
  • scalability berbeda
  • processing pattern berbeda

🔄 Flow CQRS

Flow architecture mulai berubah menjadi:

WRITE
→ append event

READ
→ query projection

Artinya:

  • websocket tidak lagi update state langsung
  • websocket cukup menulis event
  • projection dibangun asynchronous oleh worker

🧠 State Menjadi Projection

Sekarang current state:

bukan source utama

melainkan:

hasil projection dari event stream

Contoh:

  • bid event masuk ke stream
  • worker memproses event
  • projection highest bid diupdate
  • query membaca projection tersebut

⚡ Realtime Flow Menjadi Lebih Ringan

Karena write flow sekarang:

  • tidak perlu query berat
  • tidak perlu rebuild state
  • tidak perlu logic read kompleks

Akibatnya:

event append menjadi sangat ringan

Dan ini cocok untuk realtime architecture dengan traffic tinggi.

🔄 Read dan Write Sekarang Bisa Diskalakan Terpisah

CQRS membantu:

  • write system fokus durability
  • read system fokus query speed
  • projection bisa dibuat berbeda-beda

Misalnya:

  • leaderboard projection
  • highest bid projection
  • analytics projection

semua bisa dibangun dari:

event stream yang sama

🧠 CQRS Sangat Cocok Dengan Event Sourcing

Karena:

  • event sourcing menyimpan history
  • CQRS memisahkan processing responsibility

Dan kombinasi keduanya membuat architecture:

  • lebih modular
  • lebih scalable
  • lebih replay-friendly

⚡ Sistem Mulai Menjadi Event-Native

Sebelumnya:

client
→ update state langsung

Sekarang:

client
→ append event
→ projection update
→ query state

Dan itu membuat system:

  • lebih asynchronous
  • lebih recoverable
  • lebih event-driven

🎯 Posisi Kita Sekarang

Sekarang sistem kita mulai memiliki:

  • event as source of truth
  • projection-based state
  • separated write flow
  • separated read flow

Dan ini membawa architecture kita semakin dekat ke:

event-native distributed realtime platform.

4. Implementasi Event Store Sederhana

Redis Stream Sebagai Event Store

Sampai di titik ini, Redis Stream kita sebenarnya sudah:

  • menyimpan event
  • mendistribusikan processing
  • membantu replay recovery

Namun sekarang kita akan mulai mengubah cara pandang architecture:

stream bukan lagi sekadar pipeline, tetapi event store utama system

Artinya:

  • event tidak hanya lewat lalu hilang
  • event menjadi canonical history
  • state dibangun dari replay event tersebut

⚡ Redis Stream Sekarang Menjadi Append-Only Event Store

Setiap bid sekarang dianggap sebagai:

immutable event

Contoh:

andi bid 100
budi bid 200
charlie bid 300

⚡ Struktur Project Baru

Sekarang kita menggunakan folder baru:

golang/
└── example/
    └── cqrs/

✍️ Tambahkan Event Append Helper

Buka redis.go

Tambahkan helper baru:

func AppendBidEvent(room string,message []byte)
dan 
func ReplayBidHistory(room string) 

⚡ Sekarang WebSocket Tidak Lagi Menyimpan State

Sebelumnya:

websocket
→ update state
→ publish

Sekarang:

websocket
→ append event

Dan processing state dipindahkan ke:

projection worker

🔄 State Mulai Dibangun Dari Replay Event

Karena event sekarang tersimpan penuh:

  • state bisa direbuild kapan saja
  • projection bisa direset
  • recovery menjadi replay-driven

Flow-nya menjadi:

event stream
→ replay
→ projection state

🧠 Projection Menjadi Read Model

Current state sekarang hanyalah:

hasil projection

misalnya:

  • highest bid
  • latest bidder
  • leaderboard

Dan projection itu bisa dibangun ulang dari:

event history yang sama

⚡ Event Store Membuat System Lebih Recoverable

Karena event history tetap tersimpan:

  • state bisa dipulihkan
  • projection bisa direbuild
  • processing bisa diulang

Dan itu membuat architecture:

  • lebih replay-friendly
  • lebih audit-friendly
  • lebih event-native

🎯 Posisi Kita Sekarang

Sekarang sistem kita mulai memiliki:

  • append-only event store
  • replay-driven projection
  • immutable event history
  • event-centric processing

Dan ini membawa architecture kita semakin dekat ke:

CQRS dan event-sourced distributed realtime platform.

5. Update Realtime Flow

WebSocket Menjadi Pure Event Producer

Sekarang kita akan mulai mengubah realtime flow agar:

websocket tidak lagi mengupdate state langsung

Karena dalam CQRS dan event sourcing:

  • websocket hanya menghasilkan event
  • worker membangun projection
  • state dibaca dari projection tersebut

🔄 Sebelumnya WebSocket Menangani Banyak Hal

Flow lama:

websocket
→ process bid
→ update state
→ publish realtime

Masalahnya:

  • realtime flow menjadi berat
  • write dan read tercampur
  • processing sulit dipisahkan

⚡ Sekarang WebSocket Hanya Append Event

Flow baru menjadi:

websocket
→ append event

Dan semua processing state dipindahkan ke:

projection worker

✍️ Update websocket.go

Cari bagian:

err := PushBidToStream(
 room,
 message,
)

Ganti menjadi:

err := AppendBidEvent(
 room,
 message,
)

🧠 Apa yang Berubah?

Sebelumnya:

event langsung mempengaruhi state

Sekarang:

event hanya disimpan ke event store

Dan projection state dibangun asynchronous oleh worker.

⚡ Worker Sekarang Menjadi Projection Builder

Worker bertanggung jawab untuk:

  • membaca event
  • memproses history
  • membangun current state
  • update projection

Flow sekarang menjadi:

event stream
→ worker
→ projection state

🔄 Read dan Write Mulai Benar-Benar Terpisah

Sekarang:

WRITE FLOW

event stream
→ worker
→ projection state

READ FLOW

client
→ query projection

Dan keduanya:

tidak lagi tightly coupled

⚡ Realtime Flow Menjadi Lebih Ringan

Karena websocket sekarang:

  • tidak update state langsung
  • tidak menjalankan projection logic
  • tidak memproses query

Akibatnya:

append event menjadi lightweight

Dan ini sangat cocok untuk:

  • high throughput realtime system
  • distributed processing
  • asynchronous architecture

🧠 Projection Sekarang Bisa Dibangun Ulang

Karena source of truth sekarang adalah:

event history

maka:

  • projection bisa dihapus
  • state bisa direbuild
  • replay bisa dilakukan kapan saja

⚡ System Mulai Menjadi Event-Native

Sebelumnya:

state
→ source of truth

Sekarang:

event
→ source of truth

Dan state hanyalah:

Dan state hanyalah:

yang dibangun dari event stream.

🎯 Posisi Kita Sekarang

Sekarang sistem kita sudah memiliki:

  • append-only event store
  • separated write flow
  • separated read flow
  • projection worker
  • replay-driven state

Dan ini membawa architecture kita semakin dekat ke:

CQRS dan event-sourced distributed realtime platform.

6. Testing Event Replay dan Projection

Rebuild State dari Event History

Sekarang kita akan menguji:

apakah state benar-benar bisa dibangun ulang dari event history

Karena dalam event sourcing:

event adalah source of truth

sedangkan:

state hanyalah projection

▶️ Jalankan Server

Terminal 1:

go run . 8080 laptop worker-1 1 2

Expected output:

mode : PRIMARY

▶️ Kirim Beberapa Event

Terminal 2:

wscat -c ws://localhost:8080/ws?room=laptop

Kirim beberapa bid:

{"user":"andi","price":1350}
{"user":"budi","price":1351}
{"user":"charlie","price":1352}

⚡ Observe Projection Update

Cek terminal server.

Expected output:

processed bid : andi 1350
processed bid : budi 1351
processed bid : charlie 1352

Artinya:

  • event berhasil masuk stream
  • worker membangun projection
  • current state berhasil diupdate

▶️ Simulasikan State Hilang

Stop server:

CTRL + C

Lalu reset in-memory state.

Contoh di sini :

 // if err := RecoverFromSnapshot(room); err != nil {
 //  log.Fatalf("RecoverFromSnapshot:%v", err)
 // }

 if err := ReplayBidHistory(room); err != nil {
  log.Fatalf("ReplayBidHistory:%v", err)
 }

▶️ Jalankan Server Lagi

go run . 8080 laptop worker-1 1 2

🔄 Observe Replay Recovery

Expected output:

🧠 Yang Sedang Kita Buktikan

Di tahap ini kita membuktikan bahwa:

state bukan source utama

Karena walaupun:

  • memory hilang
  • state direset
  • server restart

system tetap bisa:

rebuild projection dari event history

⚡ Replay Sekarang Menjadi Capability Penting

Karena event history tetap tersimpan:

  • projection bisa dipulihkan
  • state bisa dibangun ulang
  • processing bisa direcover kapan saja

Flow sekarang menjadi:

event history
→ replay
→ rebuild projection

🔄 CQRS dan Event Sourcing Mulai Terlihat Jelas

Sekarang:

  • websocket hanya append event
  • worker membangun projection
  • replay memulihkan state

Dan architecture mulai benar-benar:

event-centric

⚡ Projection Menjadi Disposable

Ini insight penting.

Karena source utama sekarang adalah:

event stream

maka projection:

  • bisa dihapus
  • bisa dibangun ulang
  • bukan canonical source lagi

🎯 Posisi Kita Sekarang

Sekarang sistem kita sudah memiliki:

  • append-only event store
  • replay-driven recovery
  • projection-based state
  • CQRS separation
  • event-centric architecture

Dan ini membawa architecture kita semakin dekat ke:

fully event-native distributed realtime platform.

7. Insight

State Bisa Dibangun Ulang, Event Tidak

Di bagian ini, kita mulai melihat perubahan besar dalam cara memandang system:

state bukan lagi aset utama architecture

Karena dalam event sourcing:

state hanyalah hasil projection

sedangkan:

event history adalah canonical truth

⚡ State Bisa Hilang

Dalam distributed system:

  • memory bisa reset
  • process bisa restart
  • projection bisa rusak
  • cache bisa hilang

Tetapi selama:

event history masih ada

maka:

  • state bisa direbuild
  • projection bisa dipulihkan
  • processing bisa direplay

🧠 Replay Menjadi Capability Inti

Sebelumnya replay hanya:

recovery helper

Sekarang replay menjadi:

core architecture capability

karena replay memungkinkan system:

  • membangun ulang state
  • memulihkan projection
  • melakukan recovery kapan saja

⚡ Event Menjadi Immutable History

Berbeda dengan state yang terus berubah:

event tidak di-overwrite

History system tetap tersimpan:

  • siapa melakukan apa
  • kapan event terjadi
  • bagaimana state berubah

Dan dari history itulah:

current state dibentuk

🔄 CQRS Membuat Flow Lebih Fleksibel

Karena:

  • write hanya append event
  • read membaca projection

maka:

  • processing menjadi lebih modular
  • projection bisa dibuat berbeda-beda
  • replay menjadi lebih mudah dilakukan

🧠 Event-Native Architecture Lebih Recoverable

Sebelumnya:

system bergantung pada current state

Sekarang:

system bergantung pada event history

Dan itu membuat architecture:

  • lebih replay-friendly
  • lebih audit-friendly
  • lebih resilient terhadap kehilangan state

⚡ Insight Paling Penting

Dalam event-sourced system:

state bisa dibangun ulang kapan saja, tetapi event history tidak boleh hilang

Karena event adalah:

sumber utama kebenaran system

sedangkan state hanyalah:

hasil sementara dari replay event

🎯 Posisi Kita Sekarang

Sekarang sistem kita sudah memiliki:

  • append-only event store
  • replay-driven recovery
  • CQRS separation
  • projection lifecycle
  • event-centric architecture

Dan ini membawa architecture kita semakin dekat ke:

fully event-native distributed realtime platform.

8. Keterbatasan

Masalah yang Masih Ada

Walaupun event sourcing dan CQRS membuat system lebih replay-friendly dan recoverable, masih ada beberapa masalah yang belum terselesaikan.

⚠️ Replay Bisa Menjadi Mahal

Semakin banyak event:

  • replay semakin lama
  • startup recovery semakin berat
  • projection rebuild semakin mahal

Karena itu production system biasanya:

menggabungkan replay dan snapshot

⚠️ Projection Bisa Tidak Konsisten

Karena projection dibangun asynchronous:

  • read model bisa tertinggal
  • state tidak selalu realtime sempurna
  • eventual consistency mulai muncul

⚠️ Event History Tidak Mudah Diubah

Dalam event sourcing:

event bersifat immutable

Artinya:

  • event lama tidak mudah diedit
  • schema evolution menjadi lebih kompleks
  • migration history bisa sulit

⚠️ Duplicate Event Masih Mungkin Terjadi

Dalam distributed system:

  • retry bisa terjadi
  • worker bisa replay ulang
  • failover bisa memproses event yang sama

Karena itu:

idempotency

mulai menjadi penting.

🧠 Insight Penting

Event sourcing membantu:

  • replay recovery
  • auditability
  • traceability
  • projection rebuild

Tetapi:

event-centric architecture juga membawa complexity baru dalam replay, consistency, dan event management

🎯 Posisi Kita Sekarang

Sekarang sistem kita sudah:

  • event-centric
  • replay-driven
  • projection-based
  • CQRS-aware

Namun belum:

  • fully consistent
  • idempotent-safe
  • optimized untuk replay besar
  • siap untuk schema evolution.

9. Closing

Dari Resilient System ke Event-Native Architecture

Dengan Event Sourcing dan CQRS, sistem kita sekarang tidak lagi bergantung pada:

current state sebagai source utama

Sebaliknya:

event history

mulai menjadi:

  • canonical source of truth
  • replay foundation
  • recovery backbone system

Perubahan ini membawa beberapa peningkatan penting:

  • state bisa dibangun ulang
  • projection bisa direplay
  • write dan read flow mulai terpisah
  • recovery menjadi lebih fleksibel

Dan secara arsitektur, sistem kita sekarang mulai memiliki:

  • append-only event store
  • replay-driven recovery
  • CQRS separation
  • projection lifecycle
  • event-centric processing

Namun event-native architecture juga membawa tantangan baru:

  • replay cost
  • eventual consistency
  • projection synchronization
  • idempotency
  • dan event schema evolution

Karena dalam distributed realtime system:

semakin system bergantung pada event, semakin penting menjaga consistency dan replayability event tersebut.


메타데이터
post_id
d3820f2a0a46
slug
bidding-13-event-sourcing-dan-cqrs-di-golang-d3820f2a0a46
url
https://medium.com/@andriantriputra/bidding-13-event-sourcing-dan-cqrs-di-golang-d3820f2a0a46
canonical_url
https://medium.com/@andriantriputra/bidding-13-event-sourcing-dan-cqrs-di-golang-d3820f2a0a46
author_url
https://medium.com/@andriantriputra
status
ok
fetched_at
2026-06-20 20:29:01