Memastikan konsistensi transaksional dengan DDD

9

Saya memulai dengan DDD dan memahami bahwa akar agregat digunakan untuk memastikan konsistensi transnasional. Kita tidak boleh memodifikasi beberapa agregat dalam satu layanan aplikasi.

Namun saya ingin tahu bagaimana menghadapi situasi berikut.

Saya memiliki akar agregat yang disebut Produk.

Ada juga root agregat yang disebut Group.

Keduanya memiliki ID, dan dapat diedit secara mandiri.

Banyak Produk dapat mengarah ke Grup yang sama.

Saya memiliki layanan aplikasi yang dapat mengubah grup produk:

ProductService.ChangeProductGroup(string productId, string groupId)

  1. Grup cek ada
  2. Dapatkan produk dari repositori
  3. Atur grupnya
  4. Tulis produk kembali ke repositori

Saya juga memiliki layanan aplikasi tempat grup dapat dihapus:

GroupService.DeleteGroup(string groupId) 1. Dapatkan produk dari repositori yang groupIdnya disetel ke groupId yang disediakan, pastikan penghitungannya 0 atau batal 2. Hapus grup dari repositori grup 3. Simpan perubahan

Pertanyaan saya adalah skenario berikut, apa yang akan terjadi jika:

Di ProductService.ChangeProductGroup, kami memeriksa grup yang ada (memang ada), lalu tepat setelah pemeriksaan ini pengguna yang terpisah menghapus productGroup (melalui GroupService.DeleteGroup lainnya). Dalam hal ini kami menetapkan referensi ke produk yang baru saja dihapus?

Apakah ini cacat dalam desain saya karena saya harus menggunakan desain domain yang berbeda (menambahkan elemen tambahan jika perlu), atau apakah saya harus menggunakan transaksi?

morleyc
sumber

Jawaban:

2

Kami memiliki masalah yang sama.

Dan saya tidak menemukan cara untuk menyelesaikan masalah ini tetapi dengan transaksi atau dengan pemeriksaan konsistensi pada DB untuk mendapatkan pengecualian dalam kasus terburuk.

Anda juga dapat menggunakan kunci pesimistis, memblokir akar agregat atau hanya bagian-bagiannya untuk klien lain sampai transaksi bisnis diselesaikan yang pada tingkat tertentu setara dengan transaksi serial.

Cara Anda sangat bergantung pada sistem dan logika bisnis Anda.
Konkurensi bukanlah tugas yang mudah sama sekali.
Bahkan jika Anda dapat mendeteksi masalah, bagaimana Anda akan menyelesaikannya? Batalkan saja operasi atau izinkan pengguna untuk 'menggabungkan' perubahan?

Kami menggunakan Entity Framework dan EF6 menggunakan read_commited_snapshot secara default, jadi dua bacaan berurutan dari repositori dapat memberi kami data yang tidak konsisten. Kami hanya mengingatnya untuk masa depan ketika proses bisnis akan lebih jelas diuraikan dan kami dapat membuat keputusan. Dan ya kami masih memeriksa konsistensi pada level model seperti yang Anda lakukan. Setidaknya ini memungkinkan untuk menguji BL secara terpisah dari DB.

Saya juga menyarankan Anda untuk memikirkan konsistensi repositori jika Anda memiliki transaksi bisnis 'lama'. Ternyata cukup sulit.

Pavel Voronin
sumber
1

Saya pikir ini bukan pertanyaan spesifik Desain Berbasis Domain, tapi pertanyaan tentang manajemen sumber daya.

Sumber daya hanya harus dikunci saat diperoleh .

Ini benar ketika layanan aplikasi tahu sebelumnya bahwa sumber daya yang diperoleh ( Groupagregat, dalam contoh Anda) akan diubah. Dalam Desain Berbasis Domain, penguncian sumber daya adalah aspek yang dimiliki repositori.

rucamzu
sumber
Tidak, mengunci bukanlah suatu "keharusan". Sebenarnya itu adalah hal yang cukup mengerikan untuk dilakukan dengan transaksi jangka panjang. Anda lebih baik menggunakan konkurensi optimis, yang didukung oleh setiap ORM modern. Dan hal hebat tentang konkurensi optimis adalah bahwa ia juga bekerja dengan basis data non-transaksional, asalkan mendukung pembaruan atom.
Aaronaught
1
Saya setuju dengan kelemahan penguncian di dalam transaksi yang sudah berjalan lama, tetapi skenario yang dijelaskan oleh @ g18c memberi petunjuk sebaliknya, yaitu operasi singkat atas agregat individu.
rucamzu
0

Mengapa Anda tidak menyimpan id produk di entitas Grup? Dengan cara ini Anda hanya berurusan dengan satu agregat yang akan membuat segalanya lebih mudah.

Anda kemudian perlu menerapkan beberapa jenis pola konkurensi mis. Jika Anda memilih konkurensi optimis cukup tambahkan properti versi ke entitas Grup dan berikan pengecualian jika versi tidak cocok saat memperbarui yaitu

Tambahkan produk ke grup

  1. Periksa apakah ada produk
  2. Dapatkan Grup dari repositori (termasuk properti id versinya)
  3. Group.Add (ProductId)
  4. Simpan Grup menggunakan repositori (perbarui dengan id versi baru dan tambahkan klausa di mana untuk memastikan versi tidak berubah.)

Banyak ORM memiliki konkurensi optimis bawaan menggunakan id versi atau cap waktu tetapi mudah untuk menggulirkan Anda sendiri. Berikut adalah posting yang bagus tentang bagaimana hal ini dilakukan di Nhibernate http://ayende.com/blog/3946/nhibernate-mapping-concurrency .

Scott Millett
sumber
0

Meskipun transaksi mungkin membantu, ada cara lain, terutama jika skenario Anda terbatas hanya pada beberapa kasus.

Anda dapat menyertakan pemeriksaan dalam kueri yang melakukan mutasi, secara efektif menjadikan setiap pasangan periksa dan mutasi sebagai operasi atom.

Bagian dalam permintaan pembaruan produk bergabung dengan grup (baru direferensikan). Ini menyebabkannya tidak memperbarui apa pun jika grup itu hilang.

Kueri penghapusan grup bergabung dengan produk yang menunjuk ke sana dan memiliki kondisi tambahan bahwa (salah satu kolom) hasil gabungan harus nol. Ini menyebabkannya tidak menghapus apa pun jika ada produk yang menunjukkannya.

Kueri mengembalikan jumlah baris yang cocok atau diperbarui. Jika hasilnya 0, maka Anda kehilangan kondisi balapan dan tidak melakukan apa pun. Itu bisa melempar pengecualian, dan lapisan aplikasi dapat menangani yang diinginkan, seperti dengan mencoba lagi dari awal.

Timo
sumber