[Bidding #13] Event Sourcing dan CQRS di Golang
Memisahkan write model dan read model untuk membangun sistem realtime yang lebih scalable dan maintainable
[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