- Model CRUD relasional memang memperlihatkan struktur penyimpanan dengan baik, tetapi mudah menimpa proses bisnis, sehingga sulit melacak apa yang sebenarnya terjadi di sistem
- Event Sourcing meninggalkan event yang immutable yang terjadi setelah tiap operasi sebagai Event Stream, lalu membaca daftar itu dalam pengambilan keputusan berikutnya untuk menentukan status saat ini
- Pemodelan dimulai dengan menemukan event terlebih dahulu, lalu menghubungkan command, yaitu niat tindakan pengguna, dengan aturan bisnis untuk memahami proses
- Saat mencari kandidat event dari data relasional yang ada, periksa kolom status, kolom tanggal, apakah kolom nullable, dan relasi 1:N, tetapi berbahaya jika menganggap riwayat lengkap dapat dipulihkan hanya dari nilai status
- Saat memindahkan data yang hanya menyisakan status akhir, lebih realistis untuk memulai dengan event import eksplisit seperti Order Imported daripada memaksakan rekonstruksi event masa lalu, lalu melakukan validasi berulang di lingkungan yang aman
Melihat Data CRUD sebagai Model Berpusat pada Event
- Model data relasional menunjukkan data apa yang disimpan, tetapi sulit memahami apa yang terjadi di dalam sistem dan bagaimana proses saling berinteraksi
- Pendekatan CRUD yang ada dapat menghilangkan informasi bisnis penting karena menimpa data
- Event Sourcing memprioritaskan kualitas informasi dibanding ukuran penyimpanan, dan menyimpan fakta yang terjadi setelah tiap operasi sebagai event
Model Dasar Event Sourcing
- Event adalah fakta tentang sesuatu yang sudah terjadi, dan merupakan data immutable yang tidak dapat diubah setelah disimpan
- Event Stream adalah daftar berurutan dari semua hal yang terjadi pada satu record
- Event masa lalu tidak dapat diubah, tetapi kesalahan sebelumnya dapat dikoreksi dengan menambahkan event baru di bagian akhir
- Saat mengambil keputusan, daftar event dibaca dan diperiksa untuk menentukan status saat ini serta tindakan berikutnya
Urutan Pemodelan Proses
- Pemodelan dimulai terlebih dahulu dari penemuan event
- Setelah itu, temukan command dan definisikan niat untuk menjalankan tindakan tertentu
- Terakhir, susun aturan bisnis
- Event menjadi poros utama bagi pihak teknis dan pihak bisnis untuk memahami proses bersama-sama
- Seperti EventStorming dari Alberto Brandolini, proses dapat dipahami dengan melihat event, command, dan aturan secara bersama-sama
Mencari Kandidat Event dari Data Relasional yang Ada
-
1. Periksa Kolom Status
- Nilai pada kolom status dapat mencerminkan tahap dalam siklus hidup data
- Jika order memiliki status seperti initiated, shipped, paid, masing-masing dapat menjadi kandidat event Order Initiated, Order Shipped, Order Paid
- Namun, nilai status bisa jadi interpretasi yang meratakan proses bisnis, jadi jangan menganggapnya lengkap
- Hindari penamaan event seperti Order Created, Order Updated, Order Deleted yang mengikuti operasi CRUD
- State Obsession disajikan sebagai pola yang harus dihindari
-
2. Periksa Kolom Tanggal
- Kolom tanggal dapat menunjukkan waktu terjadinya peristiwa penting dalam siklus hidup proses
- CreatedDate dan ModifiedDate tidak memberi banyak informasi, tetapi ShipmentDate, DeliveryDate, OrderPlacementDate menjadi petunjuk yang lebih baik
- Contoh:
- ShipmentDate dapat menjadi petunjuk untuk memperkenalkan event Order Shipped
- OrderPlacementDate mengisyaratkan bahwa Order Placed mungkin nama yang lebih baik daripada Order Initiated
- DeliveryDate menunjukkan bahwa event Order Delivered mungkin diperlukan
- Petunjuk seperti ini harus dikonfirmasi dengan pakar domain agar selaras dengan proses bisnis yang sebenarnya
-
3. Analisis Apakah Kolom Nullable
- Kolom non-nullable adalah data yang harus selalu disediakan
- Kolom nullable bisa disediakan belakangan oleh operasi lain atau merupakan nilai opsional
- Jika sebuah kolom wajib dalam Ordering Process, data tersebut juga harus disertakan dalam event awal Order Initiated
- Satu tipe event tidak selalu menjadi titik awal stream; event awal bisa lebih dari satu
-
4. Cari Tabel dengan Banyak Relasi 1:N
- Untuk menemukan batas stream, mulailah dengan melihat tabel yang memiliki banyak relasi 1:N
- Tabel dengan banyak relasi “one” menjadi kandidat tipe stream
- Perlu juga menilai secara logis apakah data dapat eksis secara independen satu sama lain
- shipment bisa menjadi proses terpisah dari order
- order line sulit eksis tanpa order
- Dalam proses mendiskusikan batas, Anda dapat menemukan lebih banyak event dan memperluas pemahaman proses
Jangan Membuat Event Palsu Saat Migrasi
- Data relasional adalah status akhir yang sudah diratakan, sehingga jika mencoba menebak kembali event masa lalu yang detail hanya dari status tersebut, hasilnya bisa gagal atau tidak akurat
- Daripada memaksakan pembuatan event masa lalu berukuran kecil, lebih baik menyediakan event Order Imported secara eksplisit yang berisi seluruh status saat ini dan kode interpretasinya
- Event import menunjukkan dengan jelas bagaimana data masuk, dan bisa penting untuk pemecahan masalah serta diagnosis
Validasi dengan Prototipe
- Migrasi harus dicoba sebagai prototipe di lingkungan yang aman, lalu periksa bagaimana model benar-benar bergerak
- Hasilnya harus dibandingkan dengan nilai yang diharapkan dan diperbaiki secara berulang
- Diperlukan pendekatan yang tidak terburu-buru, tidak menghilangkan informasi yang ada, dan menggunakan informasi tersebut untuk memperbaiki model berikutnya
- Strategi umum untuk berpindah dari data relasional ke berbasis dokumen juga terkait dengan General strategy for migrating relational data to document-based
1 komentar
Komentar Hacker News
2c: Jika bagian lain dari aplikasi juga membutuhkan PostgreSQL, sebaiknya simpan juga data event di PostgreSQL + alat pelaporan FOSS (Apache Superset, Metabase, dll.) dan bertahan kira-kira sampai 2TB
Setelah itu, putuskan apakah seluruh 2TB perlu tetap online, atau cukup ringkasan per hari/jam. Jika yang terakhir, terus memakai PostgreSQL pun sudah memadai[1]
Salah satu pelanggan menangani skala 10TB+ dengan 1.500 event per detik, 600 byte per record (80GB per hari sebelum indexing), hanya menyimpan data detail 2 hari secara online, sementara sisanya diringkas dan detailnya dipindahkan ke S3 lalu tetap bisa di-query dengan Athena SQL[2]
Termasuk portal pelaporan untuk pelanggan, total biayanya di bawah 2 ribu dolar, dan di AWS RDS multi-AZ automatic failover (db.m7g.2xlarge), baik insert maupun query pelaporan ditangani dengan beban di bawah 2%. Karena tim bisnis membuat chart dan graph sendiri, 1 engineer menghabiskan kurang dari 5 jam per bulan untuk maintenance
Kalau memakai tool proprietary, beberapa chart memang “tersedia bawaan”, tetapi dengan pgsql, data ada di satu tempat, sistem yang perlu dipelajari satu, sistem yang perlu dijaga online/direplikasi/di-backup/dipulihkan satu, sistem yang perlu diamankan/diskalakan satu, vendor yang perlu dikelola satu, dan ada jutaan engineer yang mengenal sistem ini
Di sistem seperti Preset atau Metabase, membuat 12 chart cukup satu jam, dan staf non-teknis pun bisa melakukannya
Sebagai catatan, meski ada bias, saya sudah lebih dari 20 tahun melihat database dan sistem pelaporan muncul lalu hilang, dan PostgreSQL lama yang bagus itu terus membaik setiap tahun
https://instances.vantage.sh/aws/rds/db.m7g.2xlarge?region=u...
[1] Jika benar-benar diperlukan, ada juga sistem kompatibel PostgreSQL untuk scaling tambahan. Aurora bisa scale 3–5x, TimescaleDB 10x, CitusDB 10x+. Masing-masing ada konsekuensi menjadi agak nonstandar, jadi saya tidak merekomendasikannya sebelum benar-benar dibutuhkan
[2] Dashboard pelaporan pelanggan perlu respons di bawah 1 detik, dan ini disediakan PostgreSQL dengan meng-query tabel ringkasan yang sudah diindeks. Athena merespons sekitar 1–2 detik lewat parallel scan
Pertahankan snapshot data sebelum disimpan, siapkan script untuk mengidentifikasi dan mengumpulkan urutan event tertentu, lalu setelah ditinjau manusia, terapkan efek logic baru secara retrospektif dalam jumlah besar
Tool seperti https://django-simple-history.readthedocs.io/en/latest/ adalah solusi sederhana yang cukup bisa dipercaya untuk membuat tabel audit, dan jika akses database langsung pun perlu diaudit, trigger Postgres juga bisa ditambahkan
Secara teori saya menyukai event sourcing, tetapi dalam praktiknya ada terlalu banyak boilerplate yang diperlukan untuk menambahkan flow CRUD baru, atau untuk men-deploy intervensi dan hotfix dengan cepat dan andal dalam situasi tak terduga yang sering dialami startup tahap awal hingga menengah
Jika bukan pekerjaan seperti mengimplementasikan payment processing rails, event sourcing mungkin bukan pilihan yang tepat
Di https://news.ycombinator.com/item?id=17817375 (2018) juga ada diskusi bagus tentang kekurangan event sourcing
Satu-satunya masalah PostgreSQL adalah ada masalah skalabilitas yang menarik di sisi insert. Biasanya disarankan menaruh queue di antara event source dan DB
{id:uuid,created_at:timestamptz,data:jsonb}Terutama jika struktur event beragam dan definisi event terus berubah, sulit mendapatkan manfaat penuh dari fitur JSONB index
Mungkin saya perlu mempelajari dokumen ini lebih jauh: https://www.postgresql.org/docs/current/datatype-json.html#J...
Dulu tim saya pernah mempertimbangkan event sourcing dengan serius, tetapi bagi saya itu terlihat seperti solusi yang mencari masalah.
Mungkin saja itu bisa bekerja untuk kami, tetapi manfaatnya tidak langsung jelas, dan risiko serta trial-and-error dari mengadopsi pendekatan baru tidak tampak sebagai pilihan terbaik bagi proyek atau perusahaan, jadi akhirnya kami menyerah.
Bisa saja itu keputusan seperti melewatkan kesempatan belajar sebuah alat, tetapi saya tidak menyesal tidak masuk ke lubang kelinci itu saat tidak ada rubah yang mengejar dari belakang.
“Masalah” yang diselesaikan solusi ini ya itu.
Namun dalam kebanyakan kasus, cukup gunakan database biasa dan simpan riwayat perubahan lama di tabel pendukung. Dengan begitu, database utama berfungsi seperti semacam materialized view.
Saya tidak terlalu keberatan, dan tidak sampai menganggapnya pilihan yang salah, tetapi masalah muncul pada cara menangani perubahan model data.
Rasanya sebagian besar cara penyimpanan data tidak mampu mengikuti cara software dibuat sekarang, dan hal-hal seperti event dan queue adalah hasil dari menambahkan fungsi yang dibutuhkan di atas sistem lama.
Saat ini banyak relasi data terjadi di antara beberapa layanan, yaitu di luar database. Karena seperti itulah lingkungan IT modern di banyak organisasi.
Ada master data internal yang mendukung berbagai tim bisnis, dan berinteraksi dengan lebih dari 300 sistem IT serta aplikasi untuk menyederhanakan pekerjaan.
Dengan microservices, logika bisnis dan model data memang lebih mudah dijaga tetap rapi, tetapi sebagai gantinya kita harus mengelola event, queue, status data, hingga penyimpanan dependen, dan saat ini itu terlalu kompleks.
Saya suka SQL, tetapi jujur saja, sistem yang kita bangun belakangan ini rasanya hampir cukup jika dimasukkan ke SQLite.
Bagian yang sering hilang dari diskusi seperti ini adalah kapan arsitektur event-driven itu tepat.
Singkatnya, jika pelanggan melakukan sesuatu dan mengharapkan respons, itu bukan event-driven, melainkan sekadar request/response.
Event-driven adalah ketika sesuatu terjadi di luar alur utama. Misalnya, situasi ketika build dipicu karena kode di-push ke GitHub.
Dalam contoh ini, me-refresh halaman untuk melihat kode yang diperbarui adalah request/response, tetapi build CI yang masuk ke queue adalah event-driven.
Semoga membantu.
Dalam event sourcing atau event-driven pun Anda bisa membuat alur request-response, inline, blocking, dan siklik.
Sebaliknya, tanpa event sourcing atau event-driven pun Anda bisa membuat asinkron dengan baik menggunakan worker, queue, actor, multithreading, dan sebagainya.
Memodelkan domain event berguna untuk menjelaskan masalah yang ingin diselesaikan bersama pakar domain, dan mungkin cukup dicatat dalam dokumen saat merencanakan solusi.
Jika ingin benar-benar mengimplementasikan sistem yang menyediakan audit trail untuk state machine yang berjalan dalam jangka panjang, kemungkinan lebih baik menggunakan sesuatu seperti Temporal.io atau durable functions.
Alat semacam ini menggunakan event sourcing untuk persistensi internal, dan menyediakan model pemrograman yang menambahkan batasan berbeda pada kode yang mengorkestrasi fungsi (workflows) serta kode yang berinteraksi dengan dunia nyata (activities), sehingga memaksa kita memikirkan deduplikasi dan idempotensi.
Saya ingin mendengar saran tentang cara mengatasi masalah ini.
Konsepnya menarik, tetapi tulisannya tidak menjelaskan dengan baik cara kerjanya
Saya penasaran bagaimana status saat ini direkonstruksi secara efisien dari event stream, dan bagaimana event stream dimodelkan di database
https://www.youtube.com/watch?v=gG6DGmYKk4I
https://www.youtube.com/watch?v=jnDchr5eabI
https://www.youtube.com/watch?v=ArcypYS5XBQ
https://www.youtube.com/watch?v=uODSwR2CIV4
Ia juga mengelola contoh di GitHub
https://github.com/oskardudycz/EventSourcing.NetCore
https://github.com/oskardudycz/EventSourcing.NodeJS
https://github.com/oskardudycz/EventSourcing.JVM
Pertama, menggunakan database yang dirancang untuk kebutuhan seperti ini. Contohnya Google BigQuery, Amazon Redshift, ClickHouse, dan sebagainya
Semua data saat ini pada dasarnya adalah semacam agregasi. Dengan kata lain, seperti query group-by terhadap database event
Jika ada event, masuk akal karena secara teknis status saat ini atau status masa lalu bisa dibuat ulang dengan query agregasi
Kedua, mengganti nama penyimpanan relasional menjadi lapisan cache yang berada di samping sistem event
Secara fungsional itu hal yang sama, tetapi tidak menyalakan lampu peringatan bagi orang-orang yang terobsesi bahwa semuanya harus dibuat event-driven
Arsitektur yang dijelaskan dalam tulisan itu memang benar-benar ada. Hanya saja sangat kompleks, sehingga layanan yang memanfaatkannya biasanya melakukan pekerjaan yang sangat terarah. Bayangkan hal-hal seperti Google Analytics, Datadog, Splunk
Buat saja status yang berbeda di sistem yang berbeda sesuai kebutuhan yang berbeda
Jika membuat sistem belanja, ketika ada pembelian dan pelanggan, satu layanan bisa membaca event lalu membuat tabel relasional untuk tujuan keuangan
Layanan lain bisa membaca event lalu membuat penyimpanan key-value untuk data pelanggan, dan layanan ketiga bisa menjalankan layanan OpenSearch untuk pencarian produk
Event stream adalah daftar. Jika memakai sesuatu yang sesuai tujuan seperti Kafka, maka akan ada beberapa daftar, yaitu topic, partition, dan sebagainya
Namun itu pun bisa diselesaikan di dalam model relasional
Ini adalah perbedaan top-down vs bottom-up, atau custom vs general-purpose
Top-down berangkat dari domain bisnis, lalu memetakan implementasi di atas teknologi, alat, dan vendor yang tersedia
Bottom-up berangkat dari teknologi, alat, dan vendor yang tersedia, lalu merakitnya menjadi solusi yang berjalan
Yang custom mencakup DDD, CQRS/ES, Sagas, TBUI (task-based/driven UI), GraphQL, algebraic data type, dan sebagainya
Yang general-purpose mencakup RDBMS, CRUD, REST, transaksi ACID, CDC, UI admin umum, no-code/low-code, tipe terbatas/umum, dan sebagainya
Saya akan tetap memakai data relasional gaya lama yang bagus
Saya setuju dengan arsitektur berbasis event, tetapi tulisan ini tampaknya kesulitan menyampaikan poin utamanya
Saya akan berfokus pada perbedaan antara relasi data dan tindakan bisnis
Begitu mulai berpikir dari sudut pandang tindakan dan aktivitas bisnis, pergeseran menjauh dari penyimpanan data relasional operasional menjadi jauh lebih jelas
Event sourcing memang menarik karena punya banyak sifat yang baik
Namun bukankah tetap diperlukan relasi? Kalau begitu, bagaimana relasi itu diimplementasikan?
Jika jawabannya adalah “semuanya tersirat di dalam kode lapisan aplikasi”, itu sulit diterima
Tetap saja perlu meng-query relasi, menjaga view relasi tetap mutakhir, atau sesuatu yang sejenis
Tidak masalah jika relasi bukan inti dari model persistensi, tetapi relasi harus diimplementasikan di suatu tempat pada lapisan data, dan saya tidak melihat pembahasan itu di sini
Firestore juga punya masalah yang sama. Semua orang menangani relasi dengan satu atau lain cara, tetapi akhirnya menjadi kode aplikasi spaghetti yang tidak scalable
Jika terbiasa dengan functional programming, ini pada dasarnya sama dengan operasi fold yang melipat event stream menjadi satu status
Dari pengalaman saya menangani sistem event sourcing dulu, memang ada keuntungan berupa riwayat event yang disimpan secara eksplisit, tetapi kompleksitasnya juga bertambah banyak
Muncul masalah seperti bagaimana sebenarnya menghasilkan read model, bagaimana mengelola versi model, apakah perlu snapshot untuk read model, dan sebagainya
Dalam pengalaman saya, di sebagian besar konteks tempat pola ini diterapkan, kompleksitas tambahan itu tidak sepadan
Yang dibutuhkan adalah command queue. Command event bukanlah domain event