Saya mencoba mempelajari cara-cara DDD dan mata pelajaran terkait. Saya datang dengan ide konteks terbatas sederhana untuk menerapkan "bank": ada akun, uang dapat disimpan, ditarik dan ditransfer di antara mereka. Penting juga untuk menyimpan sejarah perubahan.
Saya mengidentifikasi entitas Akun dan sumber acara akan lebih baik untuk melacak perubahan di dalamnya. Entitas atau objek nilai lain tidak relevan dengan masalah, jadi saya tidak akan menyebutkannya.
Ketika mempertimbangkan setoran dan penarikan - ini relatif sederhana, karena hanya ada satu agregat yang dimodifikasi.
Ketika mentransfernya berbeda - dua agregat harus dimodifikasi oleh satu peristiwa MoneyTransferred . DDD tidak lagi memodifikasi beberapa agregat dalam satu transaksi. Di sisi lain aturan sumber acara adalah untuk menerapkan acara ke entitas dan memodifikasi negara berdasarkan mereka. Jika acara dapat disimpan hanya dalam database, tidak akan ada masalah. Tetapi untuk mencegah modifikasi bersamaan dari event yang bersumber dari entitas, kita harus mengimplementasikan sesuatu yang mengubah aliran acara dari setiap agregat (untuk menjaga batas transaksi mereka). Dengan versi muncul masalah lain - saya tidak bisa menggunakan struktur sederhana untuk menyimpan acara dan membacanya kembali untuk menerapkannya ke agregat.
Pertanyaan saya adalah - bagaimana saya bisa menggabungkan ketiga prinsip: "satu agregat satu transaksi", "peristiwa-> perubahan agregat" dan "pencegahan modifikasi bersamaan"?
sumber
Detail penting dalam memahami akun berbasis transaksi:
balance
atributaccount
sebenarnya adalah contoh dari denormalisasi. Itu ada untuk kenyamanan. Pada kenyataannya, saldo akun adalah jumlah transaksi, dan Anda tidak benar-benar membutuhkan akun itu sendiri untuk memiliki saldo.Mengingat hal ini, tindakan mentransfer uang tidak harus memperbarui
account
tetapi untuk memasukkantransaction
.Yang sedang berkata, ada aturan penting lainnya: tindakan menambahkan
transaction
harus atomik dengan pembaruan ke (bidang keseimbangan dinormalisasi)account
.Sekarang jika saya memahami konsep agregat DDD, berikut ini sepertinya relevan:
Jadi dalam hal desain DDD saya sarankan:
Ada satu agregat untuk mewakili transfer
Agregat terdiri dari objek-objek berikut: transfer (objek root); objek root ditautkan ke dua daftar transaksi (satu untuk setiap akun); dan setiap daftar transaksi ditautkan ke satu akun.
Semua akses ke transfer harus direnungkan oleh objek root (yang
transfer
).Jika Anda mencoba menerapkan dukungan transfer asinkron, maka kode utama Anda hanya perlu khawatir tentang membuat transfer, dalam status "menunggu". Anda mungkin memiliki utas lain atau pekerjaan yang benar-benar memindahkan uang (memasukkan ke dalam riwayat transaksi, dan karenanya memperbarui saldo) dan menetapkan transfer ke "diposkan."
Jika Anda mencari untuk menerapkan transaksi transfer real-time, memblokir, maka logika bisnis harus membuat
transfer
dan objek yang akan mengkoordinasikan kegiatan lain secara real time.Dalam hal mencegah masalah konkurensi, urutan pertama bisnis harus memasukkan transaksi debit ke daftar transaksi untuk akun sumber (tentu saja memperbarui saldo). Ini harus dilakukan secara atom pada tingkat basis data (melalui prosedur tersimpan). Setelah debit terjadi, sisa transfer harus dapat berhasil terlepas dari masalah konkurensi, karena seharusnya tidak ada aturan bisnis yang mencegah kredit ke akun target.
(Di dunia nyata, rekening bank memiliki konsep memo pos yang mendukung konsep komit dua fase yang malas. Pembuatan memo pos itu ringan dan mudah, dan juga dapat diputar kembali tanpa masalah. Konversi dari posting memo ke pos sulit adalah ketika uang benar-benar bergerak - ini tidak dapat dibatalkan - dan merupakan fase kedua dari komitmen dua fase, terjadi hanya setelah semua aturan validasi diperiksa).
sumber
Saya juga sedang dalam tahap pembelajaran. Dari sudut pandang implementasi, ini adalah bagaimana saya merasa Anda akan melakukan tindakan ini.
Kirim TransferMoneyCommand yang Meningkatkan acara berikut [MoneyTransferEvent, AccountDebitedEvent]
Perhatikan sebelum memunculkan peristiwa ini, validasi perintah dangkal dan validasi logika domain perlu dilakukan, yaitu apakah akun memiliki cukup saldo?
Tetap ada acara (dengan versi) untuk memastikan tidak ada masalah konsistensi. Perhatikan bahwa mungkin ada perintah konkuren lainnya (seperti menarik semua uang) yang berhasil dan menyelamatkan acara sebelum ini, sehingga keadaan agregat saat ini mungkin kedaluwarsa dan oleh karena itu peristiwa tersebut dibesarkan pada keadaan lama dan tidak benar. Jika penyimpanan acara gagal, Anda harus mencoba kembali perintah dari awal.
Setelah acara berhasil disimpan dalam database Anda dapat mempublikasikan dua peristiwa yang dimunculkan.
AccountDebitedEvent akan menghapus uang dari akun pembayar (memperbarui negara agregat dan model tampilan / proyeksi terkait)
MoneyTransferEvent memulai Saga / Process Manager.
Tugas saga / manajer proses adalah mencoba untuk mengkredit rekening penerima pembayaran, jika gagal, perlu mengkredit saldo kembali ke pembayar.
Saga / Manajer proses akan menerbitkan CreditAccountCommand yang diterapkan ke akun penerima pembayaran dan jika berhasil daripada AccountCreditedEvent akan dinaikkan.
Dari sudut pandang sumber acara, jika Anda ingin membalikkan tindakan ini, semua peristiwa dalam transaksi ini akan memiliki id korelasi / penyebab sebagai TransferMoneyCommand asli yang dapat Anda gunakan untuk meningkatkan acara untuk operasi undo / pembalikan.
Jangan ragu untuk menyarankan masalah atau potensi perbaikan di atas.
sumber