Event Sourcing: Membangun Sistem yang Dapat Diaudit
Daftar Isi
- Pendahuluan
- Apa Itu Event Sourcing?
- State-Based vs Event-Based Thinking
- Contoh Sederhana
- Projection: Mengubah Event Menjadi Read Model
- Kenapa Tim Memakai Event Sourcing?
- Trade-Off
- Kapan Event Sourcing Cocok Dipakai?
- Kesalahan Umum
- Checklist
- FAQ
- Kesimpulan
Pendahuluan
Pada umumnya, aplikasi bisnis menyimpan state terbaru dari setiap transaksi atau perubahan yang terjadi. Contohnya, Record user menyimpan email terbaru. Order menyimpan status terbaru. Account menyimpan balance saat ini.
Event sourcing berangkat dari pertanyaan yang berbeda: bagaimana kalau hal terpenting yang disimpan bukan hanya state terbaru, tetapi riwayat perubahan yang membentuk state tersebut?
Alih-alih hanya menyimpan “status order adalah shipped”, sistem event-sourced menyimpan fakta seperti OrderCreated, PaymentCaptured, AddressChanged, dan OrderShipped. State terbaru bisa dibangun ulang dari event-event tersebut.
Pattern ini sangat cocok untuk sistem yang membutuhkan audit trail, riwayat bisnis, dan keputusan yang bisa dilacak. Namun pattern ini juga kadang digunakan secara berlebihan, apalagi untuk aplikasi yang tidak menuntut kompleksitas seperti itu. Yang paling penting adalah kita memahami plus minusnya.
Apa Itu Event Sourcing?
Event sourcing menyimpan urutan business event yang immutable sebagai source of truth.
Event adalah sesuatu yang sudah terjadi:
AccountOpenedMoneyDepositedMoneyWithdrawnOrderCancelledSubscriptionRenewed
Penamaan event itu sangat penting. Event adalah fakta, bukan perintah, jadi gunakan kata kerja lampau. Contohnya CancelOrder (Batalkan Pesanan) adalah request, sedangkan OrderCancelled (Pesanan Dibatalkan) adalah fakta bahwa sistem telah menerima dan mencatat pembatalan tersebut.
State-Based vs Event-Based Thinking
Dalam model state-based, biasanya kita menyimpan data dalam bentuk terbaru:
| account_id | balance |
|---|---|
| A100 | 175 |
Dalam model event-based, kita menyimpan perubahan:
| sequence | event_type | amount |
|---|---|---|
| 1 | AccountOpened | 0 |
| 2 | MoneyDeposited | 100 |
| 3 | MoneyDeposited | 75 |
Balance terbaru dihitung dari event stream:
0 + 100 + 75 = 175
Event stream menjelaskan bagaimana sistem sampai ke state saat ini.
Contoh Sederhana
Bayangkan sistem e-wallet. Table tradisional mungkin menyimpan:
| wallet_id | balance |
|---|---|
| W01 | 120 |
Nilai itu berguna, tapi tidak menjelaskan apa yang terjadi. Dengan event sourcing, wallet punya stream event:
| version | event_type | amount | catatan |
|---|---|---|---|
| 1 | WalletCreated | 0 | wallet awal |
| 2 | FundsAdded | 200 | top-up |
| 3 | FundsSpent | 80 | pembelian |
State terbaru bisa dibangun ulang dengan cara me-replay event stream berikut:
200 - 80 = 120
Kalau user bertanya kenapa balance-nya 120, sistem bisa menjawab dengan riwayat, bukan tebakan.
Projection: Mengubah Event Menjadi Read Model
Aplikasi tetap butuh proses read yang cepat. Kita biasanya tidak ingin me-replay semua event setiap kali user membuka dashboard.
Karena itu, sistem event-sourced memakai projection. Projection adalah view atau read model yang dibangun dari event stream. Projection membaca event dan membangun model yang cepat untuk di-query. Pola ini berpasangan erat dengan CQRS Pattern untuk memisahkan alur kerja write dan read secara terstruktur.
| Event | Update ke projection |
|---|---|
FundsAdded(200) | tambah balance wallet 200 |
FundsSpent(80) | kurangi balance wallet 80 |
FundsAdded(50) | tambah balance wallet 50 |
Projection bisa terlihat seperti table biasa:
| wallet_id | current_balance | last_event_version |
|---|---|---|
| W01 | 170 | 4 |
Event store tetap menjadi source of truth. Projection dioptimalkan untuk kebutuhan read.
Kenapa Harus Memakai Event Sourcing?
Auditability
Event sourcing secara otomatis memberi kamu audit trail yang lengkap. Kamu bisa melihat apa yang terjadi, kapan, dan juga siapa yang memicunya.
Riwayat bisnis
Beberapa domain mementingkan ataupun mewajibkan adanya riwayat transaksi: banking, billing, inventory, compliance, insurance, dan approval workflow.
Debugging Perilaku Kompleks
Saat state terlihat tidak wajar, event membantu menelusuri urutan transaksi yang menghasilkan state tersebut. Hal ini tentu jauh lebih jelas daripada hanya melihat row terakhir.
Membangun Ulang Read Model
Jika kamu butuh report, projection, atau model analytics baru, kamu bisa membangunnya ulang dari event yang sudah ada.
Trade-Off
Event sourcing memberi banyak kemampuan, tapi juga menambah kompleksitas desain dan operasional.
| Manfaat | Biaya |
|---|---|
| Audit trail kuat | Data model lebih kompleks |
| Read model bisa dibangun ulang | Projection lag dan eventual consistency |
| Riwayat bisnis jelas | Event versioning dari waktu ke waktu |
| Debugging lebih mudah | Butuh tooling dan infrastruktur tambahan |
Pertanyaannya bukan “apakah event sourcing keren?” Pertanyaan yang lebih tepat: “apakah domain ini membutuhkan history sampai kompleksitasnya sepadan?”
Kapan Event Sourcing Cocok Dipakai?
Event sourcing sangat cocok pada kasus:
- business event lebih penting daripada state terbaru saja
- audit trail wajib tersedia
- keputusan perlu dijelaskan di masa depan
- workflow punya banyak transisi
- report perlu dibangun ulang dari fakta historis
- tim siap menghadapi eventual consistency
Pattern ini biasanya kurang cocok untuk screen CRUD sederhana ketika nilai terbaru sudah cukup dan history tidak terlalu bernilai.
Kesalahan Umum
Menganggap Event Seperti Database Log
Event sebaiknya menjelaskan fakta bisnis, bukan operasi database level rendah. Contoh yang bagus: EmailChanged. Contoh yang kurang baik: UserRowUpdated, karena tidak menjelaskan fakta bisnisnya, hanya menjelaskan perubahan data saja.
Mengubah Event Lama Sembarangan
Event adalah fakta historis. Jika schema event perlu berkembang, gunakan versioning atau transformasi dengan hati-hati, jangan mengubah event lama sembarangan, karena akan merusak audit trail.
Mengabaikan Kegagalan Projection
Projection bisa gagal atau tertinggal. Pantau progress, retry, dan lag. Pastikan projection yang gagal tidak merusak read model dan selalu up-to-date.
Memakai Event Sourcing di Semua Tempat
Tidak semua table butuh event stream. Gunakan pattern ini di bagian sistem yang history-nya benar-benar bernilai, jangan memaksakannya pada system yang tidak membutuhkannya.
Lupa Idempotency
Apa itu Idempotency? Idempotency adalah properti dari suatu operasi yang mengizinkan operasi tersebut dijalankan berulang kali tanpa mengubah hasil dari operasi tersebut.
Contohnya: x = x + 1. Jika dijalankan berulang kali, hasilnya akan berbeda. Namun, x = x (tetap) akan menghasilkan nilai yang sama meskipun dijalankan berkali-kali.
Dalam konteks event sourcing, handler projection harus aman untuk di-replay. Jika diperlukan proses ganda, tidak boleh merusak read model. Idempotency adalah kunci untuk menghindari duplicate data dan memastikan data consistency. Untuk pembahasan lebih mendalam tentang penanganan request berulang di level API, kamu juga bisa membaca artikel tentang Idempotency Key.
Checklist
- Definisikan event sebagai fakta bisnis.
- Jaga event tetap immutable.
- Simpan urutan event per aggregate atau stream.
- Desain projection untuk use case read.
- Pantau version dan lag projection.
- Rencanakan evolusi schema event.
- Buat handler idempotent.
- Gunakan snapshot hanya saat replay mulai mahal.
- Pakai event sourcing untuk domain yang history-nya penting.
- Hindari menjadikannya default untuk CRUD sederhana.
FAQ
Apakah event sourcing sama dengan audit log?
Tidak. Audit log mencatat history di samping model state biasa. Dalam event sourcing, event adalah source of truth dan state terbaru diturunkan dari event.
Apakah event sourcing harus memakai CQRS?
Tidak selalu, tapi keduanya sering dipakai bersama. CQRS memisahkan write model dan read model, sehingga cocok dengan event stream dan projection.
Apakah event bisa diubah?
Event lama sebaiknya diperlakukan immutable. Jika makna berubah, gunakan versi event baru atau logic transformasi.
Apakah event sourcing hanya untuk microservices?
Tidak. Event sourcing juga bisa dipakai di modular monolith. Pattern ini soal persistence dan history, bukan jumlah service. Namun dalam arsitektur terdistribusi seperti Microservices Architecture atau Hexagonal Architecture, event sourcing sering dikombinasikan dengan Saga Pattern untuk mengelola transaksi antar service.
Kesimpulan
Event sourcing berguna ketika cerita di balik data sama pentingnya dengan data terbaru. Pattern ini memberi auditability, riwayat bisnis, dan kemampuan membangun read model dari fakta.
Tapi event sourcing tidak mudah dan gratis. Sistem ini memerlukan knowledge dan effort yang tidak sedikit. Gunakan di domain yang benar-benar membutuhkan historical truth, dan tetap pakai persistence yang lebih sederhana untuk bagian sistem yang hanya butuh state terbaru.
Bagaimana dengan sistem yang sedang kamu kembangkan saat ini? Apakah kamu pernah menerapkan event sourcing atau audit trail di project-mu?
Yuk, bagikan pengalaman, pertanyaan, atau tanggapanmu di kolom komentar di bawah!
Artikel Terkait
Lanjutkan membaca topik yang masih satu konteks.
Saga Pattern: Mengelola Distributed Transactions
Pelajari saga pattern untuk distributed transaction, termasuk choreography, orchestration, compensating action, failure handling, dan trade-off praktis.
Circuit Breaker Pattern: Membangun Sistem Resilient
Pelajari circuit breaker pattern lewat state closed, open, half-open, retry, timeout, fallback, observability, dan trade-off production.
Hexagonal Architecture: Memahami Ports dan Adapters
Pahami hexagonal architecture lewat ports, adapters, arah dependency, manfaat testing, contoh use case, trade-off, dan panduan implementasi praktis.
NoSQL vs Relational: Memilih Database yang Tepat untuk Proyek Kamu
Kerangka praktis untuk memilih database relational atau NoSQL berdasarkan bentuk data, query, konsistensi, dan kebutuhan operasional.