Sejauh yang saya mengerti, ide besar di balik CQRS adalah memiliki 2 model data yang berbeda untuk menangani perintah dan permintaan. Ini disebut "model tulis" dan "model baca".
Mari kita perhatikan contoh klon aplikasi Twitter. Berikut adalah perintahnya:
- Pengguna dapat mendaftar sendiri.
CreateUserCommand(string username)
memancarkanUserCreatedEvent
- Pengguna dapat mengikuti pengguna lain.
FollowUserCommand(int userAId, int userBId)
memancarkanUserFollowedEvent
- Pengguna dapat membuat posting.
CreatePostCommand(int userId, string text)
memancarkanPostCreatedEvent
Sementara saya menggunakan istilah "acara" di atas, saya tidak bermaksud acara 'sumber acara'. Maksud saya sinyal yang memicu pembaruan model baca. Saya tidak punya toko acara dan sejauh ini ingin berkonsentrasi pada CQRS sendiri.
Dan inilah pertanyaannya:
- Seorang pengguna perlu melihat daftar postingannya.
GetPostsQuery(int userId)
- Seorang pengguna perlu melihat daftar pengikutnya.
GetFollowersQuery(int userId)
- Seorang pengguna perlu melihat daftar pengguna yang diikuti.
GetFollowedUsersQuery(int userId)
- Seorang pengguna perlu melihat "umpan teman" - log dari semua aktivitas teman mereka ("teman Anda John baru saja membuat posting baru").
GetFriedFeedRecordsQuery(int userId)
Untuk menangani CreateUserCommand
saya perlu tahu apakah pengguna seperti itu sudah ada. Jadi, pada titik ini saya tahu bahwa model penulisan saya harus memiliki daftar semua pengguna.
Untuk menangani FollowUserCommand
saya perlu tahu apakah userA sudah mengikuti userB atau tidak. Pada titik ini saya ingin model tulis saya memiliki daftar semua koneksi pengguna-mengikuti-pengguna.
Dan akhirnya, untuk menangani CreatePostCommand
saya tidak berpikir saya perlu yang lain, karena saya tidak punya perintah seperti UpdatePostCommand
. Jika saya punya itu, saya perlu memastikan bahwa posting ada, jadi saya perlu daftar semua posting. Tetapi karena saya tidak memiliki persyaratan ini, saya tidak perlu melacak semua posting.
Pertanyaan # 1 : apakah benar menggunakan istilah "model tulis" seperti yang saya gunakan? Atau apakah "menulis model" selalu berarti "event store" untuk ES? Jika demikian, apakah ada jenis pemisahan antara data yang saya butuhkan untuk menangani perintah dan data yang saya butuhkan untuk menangani pertanyaan?
Untuk menangani GetPostsQuery
, saya perlu daftar semua posting. Ini berarti bahwa model baca saya harus memiliki daftar semua posting. Saya akan mempertahankan model ini dengan mendengarkan PostCreatedEvent
.
Untuk menangani keduanya GetFollowersQuery
dan GetFollowedUsersQuery
, saya perlu daftar semua koneksi antara pengguna. Untuk mempertahankan model ini saya akan mendengarkan UserFollowedEvent
. Berikut adalah Pertanyaan # 2 : apakah bisa dibilang OK jika saya menggunakan daftar koneksi model tulis di sini? Atau haruskah saya membuat model baca yang lebih baik, karena di masa depan saya mungkin memerlukannya untuk memiliki lebih banyak detail daripada model tulis?
Akhirnya, untuk menangani GetFriendFeedRecordsQuery
saya perlu:
- Mendengarkan
UserFollowedEvent
- Mendengarkan
PostCreatedEvent
- Ketahui pengguna mana yang mengikuti pengguna lain
Jika pengguna A mengikuti pengguna B dan pengguna B mulai mengikuti pengguna C, catatan berikut ini akan muncul:
- Untuk pengguna A: "Anda teman pengguna B baru saja mulai mengikuti pengguna C"
- Untuk pengguna B: "Anda baru saja mulai mengikuti pengguna C"
- Untuk pengguna C: "Pengguna B sekarang mengikuti Anda"
Inilah Pertanyaan # 3 : model apa yang harus saya gunakan untuk mendapatkan daftar koneksi? Haruskah saya menggunakan model tulis? Haruskah saya menggunakan model baca - GetFollowersQuery
/ GetFollowedUsersQuery
? Atau haruskah saya membuat GetFriendFeedRecordsQuery
model itu sendiri menangani UserFollowedEvent
dan mempertahankan daftar semua koneksi sendiri?
sumber
Jawaban:
Greg Young (2010)
Jika Anda berpikir dalam hal Pemisahan Perintah Bertrand Meyer , Anda dapat menganggap model memiliki dua antarmuka yang berbeda, satu yang mendukung perintah, dan satu yang mendukung permintaan.
Wawasan Greg Young adalah bahwa Anda dapat memisahkan ini menjadi dua objek yang terpisah
Setelah memisahkan objek, Anda sekarang memiliki opsi untuk memisahkan struktur data yang menyimpan status objek dalam memori, sehingga Anda dapat mengoptimalkan untuk setiap kasus; atau menyimpan / mempertahankan status baca secara terpisah dari kondisi penulisan.
Istilah WriteModel biasanya dipahami sebagai representasi model yang bisa berubah (yaitu: objek, bukan toko persistensi).
TL; DR: Saya pikir penggunaan Anda baik-baik saja.
Itu "baik" - ish. Secara konsep, tidak ada yang salah dengan model baca dan tulis yang berbagi struktur yang sama.
Dalam praktiknya, karena menulis ke model biasanya bukan atom, ada potensi masalah ketika satu utas sedang mencoba untuk mengubah keadaan model sementara utas kedua sedang mencoba untuk membacanya.
Menggunakan model tulis adalah jawaban yang salah.
Menulis beberapa model baca, di mana masing-masing disetel ke use case tertentu benar-benar masuk akal. Kami mengatakan "model baca", tetapi dipahami bahwa mungkin ada banyak model baca, masing-masing dioptimalkan untuk kasus penggunaan tertentu, dan tidak menerapkan kasus di mana itu tidak masuk akal.
Misalnya, Anda mungkin memutuskan untuk menggunakan penyimpanan nilai kunci untuk mendukung beberapa permintaan, dan basis data grafik untuk permintaan lain, atau basis data relasional di mana model kueri itu masuk akal. Kuda untuk kursus.
Dalam keadaan spesifik Anda, di mana Anda masih mempelajari polanya, saran saya adalah menjaga desain Anda "sederhana" - minta satu model baca yang tidak berbagi struktur data model tulis.
sumber
Saya akan mendorong Anda untuk mempertimbangkan bahwa Anda memiliki satu model data konseptual.
Kemudian model tulis adalah perwujudan dari model data yang dioptimalkan untuk pembaruan transaksional. Terkadang ini berarti database relasional yang dinormalisasi.
Model baca adalah perwujudan dari model data yang sama yang dioptimalkan untuk melakukan kueri yang dibutuhkan aplikasi Anda. Mungkin masih berupa basis data relasional meskipun sengaja didenormalisasi untuk menangani pertanyaan dengan lebih sedikit gabungan.
(# 1) Untuk CQRS, model tulis tidak harus menjadi toko acara.
(# 2) Saya tidak akan mengharapkan model baca untuk menyimpan apa pun yang tidak ada dalam model tulis, untuk satu karena dalam CQRS, tidak ada yang memperbarui model baca kecuali mekanisme penerusan yang membuat model baca tetap sinkron dengan perubahan pada menulis model.
(# 3) Dalam CQRS, kueri harus bertentangan dengan model baca. Anda dapat melakukan yang berbeda: tidak apa-apa, hanya saja tidak mengikuti CQRS.
Singkatnya, hanya ada satu model data konseptual. CQRS memisahkan jaringan perintah dan kemampuan dari jaringan permintaan dan kemampuan. Mengingat bahwa pemisahan model tulis dan model baca dapat dihosting menggunakan teknologi yang sangat berbeda sebagai pengoptimalan kinerja.
sumber