Architecture-center: Apa perbedaan antara ProductsCommandHandler (CQRS) dan ProductRepository (arch tradisional)?

Dibuat pada 15 Nov 2019  ·  5Komentar  ·  Sumber: MicrosoftDocs/architecture-center

Hai,
Terima kasih telah menulis artikel ini di CQRS. Saya mencoba untuk memahami bagaimana implementasinya terlihat dan saya melihat contohnya. Kode repositori sampel yang diberikan dalam contoh terlihat sangat mirip dengan cara biasanya seseorang akan menulis repo kecuali untuk konvensi penamaan. Sebagai contoh, saya pikir repositori yang diberikan di bawah ini sama dengan ProductsCommandHandler dan mungkin satu perbedaan adalah bahwa tidak ada GetProduct di sini yang biasanya kita tambahkan. Bisakah Anda menjelaskan apa yang tidak saya maksudkan di sini?

public class ProductRepository {
  void AddNewProduct(Product newProduct) {
    ...
  }
  void RateProduct(int productId, int userId, int rating) {
    var product = repository.Find(productId);
    if (product != null)
    {
      product.RateProduct(userId, rating);
      repository.Save(product);
    }
  }
}

Detail Dokumen

Jangan edit bagian ini.

Pri1 architecture-centesvc assigned-to-author cloud-fundamentalsubsvc product-question triaged

Komentar yang paling membantu

@martinmthomas Sebenarnya, penangan perintah bukan tempat penyimpanan. Ini menggunakan repositori.

Penangan perintah dimaksudkan untuk "memproses perintah yang sebenarnya, jika cocok". Dalam contoh kelas ProductsCommandHandler menerima IRepository<Product> dalam konstruktornya.

Katakanlah pengguna akan menilai produk 5555 hingga 4 bintang.

  • Pengguna melihat sebuah antarmuka. Katakanlah sebuah halaman web (bisa jadi program .exe lokal atau apa pun, bayangkan sebuah situs web).
  • Pengguna mengklik tombol. Katakanlah ini memicu AJAX POST menuju ke rute / tarif dengan data {"product":"5555","stars":4}
  • Rute POST memiliki pengontrol. Kontroler ini dikaitkan dengan operasi "tulis" karena berasal dari panggilan POST.
  • Pengontrol di sini dalam pendekatan klasik hanya akan memuat repositori produk klasik dan memuat produk 5555. Kemudian beri tahu produk "Anda sekarang diberi peringkat 4 bintang" dan simpan.
  • Dalam pendekatan ini, kontroler membangun perintah RateProduct dan mengisi produk 5555, bintang 4. Dalam contoh ini juga mengisi siapa yang memberi peringkat.
  • Pada saat ini, pengontrol "mengirimkan perintah" ke model tulis. Anda memiliki 2 opsi di sini: Panggil penangan perintah atau antrekan.
  • Katakanlah Anda tidak memiliki antrian. Anda kemudian mendapatkan CommandHandler di controller Anda (mungkin dengan injeksi ketergantungan) dan hanya menempatkan perintah di sana: h.Handle (c); di mana c adalah perintah RateProduct.
  • Katakanlah Anda memiliki antrian. Anda kemudian mendapatkan antrian di pengontrol Anda (mungkin dengan injeksi ketergantungan) dan cukup mengantrekan perintah di sana: q.Queue (c);
  • Dalam kasus terakhir ini, pendengar antrian akan mengeluarkan perintah dari antrian dan memanggil penangan perintah sebagai gantinya.
  • Sebagai opsi ketiga (disarankan), pengontrol Anda tidak memilih apakah perintah sedang diproses secara sinkron (dapatkan penangan) atau secara asinkron (dapatkan antrian) tetapi harus menggunakan "pendekatan umum" yaitu menggunakan bus perintah tempat perintah ditempatkan. Katakanlah bus perintah adalah b, maka Anda akan melakukan b.Send (c); di mana c adalah perintah RateProduct.
  • Dengan pendekatan ketiga ini Anda menempatkan pengendali perintah di ujung bus, dan Anda dapat mengkonfigurasi middlewares untuk "melepaskan perintah dari bus dan menempatkannya di antrian".

Jadi 3 opsi: a) Dapatkan pawang, b) Dapatkan antrian, c) Dapatkan bus dan biarkan ia memutuskan untuk mengirim perintah ke pawang atau ke antrian.

Apapun pendekatan yang Anda lakukan ... perbedaan utama tentang "repositori klasik" adalah bahwa Anda TIDAK PERNAH menggunakan repositori itu sendiri di kontroler. Kontroler tidak "tahu tentang cara mengelola entitas" (repositori klasik) tetapi tahu "cara mengelola NIAT MELAKUKAN HAL ke entitas" (penangan perintah).

Maka itu adalah penangan perintah yang -seperti namanya- itu "menangani perintah" yang datang dari suatu tempat (pengontrol web html, pengontrol API, baris perintah, apa pun) yang dikirim sebagai maksud dan itu adalah CommandHandler (yang termasuk sisi tulis) yang memutuskan apa yang harus dilakukan dengan perintah itu.

Misalnya, CommandHandler dapat menggunakan repo untuk mendapatkan produk, menyetel status dan menyimpan (seperti dalam contoh), tetapi juga dapat menulis ke log peristiwa, memicu elemen untuk memperbarui sisi baca, memicu konektor eksternal, atau apa pun.

UI berbasis tugas pengguna untuk menilai produk dan pengontrolnya menjadi AGNOSTIK "di mana" sistem bintang / peringkat ditempatkan atau disimpan. Bayangkan Anda mendesain sistem dari awal dan kelas Produk sudah berisi metode RateProduct () seperti dalam contoh. Baik.

Tapi ... bagaimana jika Anda memiliki sistem warisan di sana dengan pendekatan "Lama" untuk Produk. Dalam "model" (yaitu: Business Mind) tidak ada "peringkat" produk. Sebaliknya orang pemasaran ingin "menambahkan" peringkat ke produk yang ada tetapi semua perusahaan setuju bahwa ini adalah "hal eksternal". Apakah Anda akan menggunakan repositori Produk? Atau mungkin penyimpanan pembantu lain sehingga Anda tidak "menyentuh" ​​Product dan ProductRepository yang telah teruji sepenuhnya dan sudah teruji?

Jika Anda menggunakan perintah, tidak masalah bagi pengontrol penulisan. Web, API, dan CLI semuanya hanya akan mengirim "perintah" ke pengendali perintah (baik secara langsung, melalui antrian atau melalui bus perintah yang akan merutekan secara bergiliran ke penangan atau ke antrian) dan mereka lupa. Kemudian penangan perintah akan memutuskan apa yang harus dilakukan dengan "RateProductCommand" di titik kecil dan terpusat dari kode sumber Anda, ini memisahkan cara ini ditangani dari kode aplikasi, sehingga mendapatkan mantainability.

Kemudian penangan akan memutuskan apakah cocok menggunakan ProductRepository atau pendekatan lain apa pun untuk menyimpan "peringkat produk".

Jadi, untuk menjawab:

CommandHandler => tidak ada hubungannya dengan entitites. Ini menangani "niat pengguna" (yang pada akhirnya dapat mengubah entitas; jadi kemungkinan besar penangan perintah menggunakan repositori).
Repositori => penyimpanan sebenarnya untuk entitas tertentu.

Harapan untuk membantu.
Xavi.

Semua 5 komentar

@martinmthomas Terima kasih atas pertanyaan Anda! Kami akan meninjau dan memberikan pembaruan yang sesuai.

@MikeWasson ada pemikiran di sini?

AB # 160217 - Terima kasih telah melaporkan - masalah ini sedang ditinjau

@martinmthomas Sebenarnya, penangan perintah bukan tempat penyimpanan. Ini menggunakan repositori.

Penangan perintah dimaksudkan untuk "memproses perintah yang sebenarnya, jika cocok". Dalam contoh kelas ProductsCommandHandler menerima IRepository<Product> dalam konstruktornya.

Katakanlah pengguna akan menilai produk 5555 hingga 4 bintang.

  • Pengguna melihat sebuah antarmuka. Katakanlah sebuah halaman web (bisa jadi program .exe lokal atau apa pun, bayangkan sebuah situs web).
  • Pengguna mengklik tombol. Katakanlah ini memicu AJAX POST menuju ke rute / tarif dengan data {"product":"5555","stars":4}
  • Rute POST memiliki pengontrol. Kontroler ini dikaitkan dengan operasi "tulis" karena berasal dari panggilan POST.
  • Pengontrol di sini dalam pendekatan klasik hanya akan memuat repositori produk klasik dan memuat produk 5555. Kemudian beri tahu produk "Anda sekarang diberi peringkat 4 bintang" dan simpan.
  • Dalam pendekatan ini, kontroler membangun perintah RateProduct dan mengisi produk 5555, bintang 4. Dalam contoh ini juga mengisi siapa yang memberi peringkat.
  • Pada saat ini, pengontrol "mengirimkan perintah" ke model tulis. Anda memiliki 2 opsi di sini: Panggil penangan perintah atau antrekan.
  • Katakanlah Anda tidak memiliki antrian. Anda kemudian mendapatkan CommandHandler di controller Anda (mungkin dengan injeksi ketergantungan) dan hanya menempatkan perintah di sana: h.Handle (c); di mana c adalah perintah RateProduct.
  • Katakanlah Anda memiliki antrian. Anda kemudian mendapatkan antrian di pengontrol Anda (mungkin dengan injeksi ketergantungan) dan cukup mengantrekan perintah di sana: q.Queue (c);
  • Dalam kasus terakhir ini, pendengar antrian akan mengeluarkan perintah dari antrian dan memanggil penangan perintah sebagai gantinya.
  • Sebagai opsi ketiga (disarankan), pengontrol Anda tidak memilih apakah perintah sedang diproses secara sinkron (dapatkan penangan) atau secara asinkron (dapatkan antrian) tetapi harus menggunakan "pendekatan umum" yaitu menggunakan bus perintah tempat perintah ditempatkan. Katakanlah bus perintah adalah b, maka Anda akan melakukan b.Send (c); di mana c adalah perintah RateProduct.
  • Dengan pendekatan ketiga ini Anda menempatkan pengendali perintah di ujung bus, dan Anda dapat mengkonfigurasi middlewares untuk "melepaskan perintah dari bus dan menempatkannya di antrian".

Jadi 3 opsi: a) Dapatkan pawang, b) Dapatkan antrian, c) Dapatkan bus dan biarkan ia memutuskan untuk mengirim perintah ke pawang atau ke antrian.

Apapun pendekatan yang Anda lakukan ... perbedaan utama tentang "repositori klasik" adalah bahwa Anda TIDAK PERNAH menggunakan repositori itu sendiri di kontroler. Kontroler tidak "tahu tentang cara mengelola entitas" (repositori klasik) tetapi tahu "cara mengelola NIAT MELAKUKAN HAL ke entitas" (penangan perintah).

Maka itu adalah penangan perintah yang -seperti namanya- itu "menangani perintah" yang datang dari suatu tempat (pengontrol web html, pengontrol API, baris perintah, apa pun) yang dikirim sebagai maksud dan itu adalah CommandHandler (yang termasuk sisi tulis) yang memutuskan apa yang harus dilakukan dengan perintah itu.

Misalnya, CommandHandler dapat menggunakan repo untuk mendapatkan produk, menyetel status dan menyimpan (seperti dalam contoh), tetapi juga dapat menulis ke log peristiwa, memicu elemen untuk memperbarui sisi baca, memicu konektor eksternal, atau apa pun.

UI berbasis tugas pengguna untuk menilai produk dan pengontrolnya menjadi AGNOSTIK "di mana" sistem bintang / peringkat ditempatkan atau disimpan. Bayangkan Anda mendesain sistem dari awal dan kelas Produk sudah berisi metode RateProduct () seperti dalam contoh. Baik.

Tapi ... bagaimana jika Anda memiliki sistem warisan di sana dengan pendekatan "Lama" untuk Produk. Dalam "model" (yaitu: Business Mind) tidak ada "peringkat" produk. Sebaliknya orang pemasaran ingin "menambahkan" peringkat ke produk yang ada tetapi semua perusahaan setuju bahwa ini adalah "hal eksternal". Apakah Anda akan menggunakan repositori Produk? Atau mungkin penyimpanan pembantu lain sehingga Anda tidak "menyentuh" ​​Product dan ProductRepository yang telah teruji sepenuhnya dan sudah teruji?

Jika Anda menggunakan perintah, tidak masalah bagi pengontrol penulisan. Web, API, dan CLI semuanya hanya akan mengirim "perintah" ke pengendali perintah (baik secara langsung, melalui antrian atau melalui bus perintah yang akan merutekan secara bergiliran ke penangan atau ke antrian) dan mereka lupa. Kemudian penangan perintah akan memutuskan apa yang harus dilakukan dengan "RateProductCommand" di titik kecil dan terpusat dari kode sumber Anda, ini memisahkan cara ini ditangani dari kode aplikasi, sehingga mendapatkan mantainability.

Kemudian penangan akan memutuskan apakah cocok menggunakan ProductRepository atau pendekatan lain apa pun untuk menyimpan "peringkat produk".

Jadi, untuk menjawab:

CommandHandler => tidak ada hubungannya dengan entitites. Ini menangani "niat pengguna" (yang pada akhirnya dapat mengubah entitas; jadi kemungkinan besar penangan perintah menggunakan repositori).
Repositori => penyimpanan sebenarnya untuk entitas tertentu.

Harapan untuk membantu.
Xavi.

Seperti yang disebutkan @xmontero , ProductsCommandHandler dan ProductRepository hanya memiliki relasi "memiliki". ProductsCommandHandler bertindak sebagai antarmuka antara ProductApi / Controller dan ProductRepository. Implementasi ini membantu menjaga agar perintah tetap terpisah dari Queries. Dalam pendekatan tradisional, kami langsung menggunakan repositori dari pengontrol yang menjaga menjalankan perintah dan Kueri bersama-sama sehingga tidak perlu antarmuka lain di antaranya.

Anda telah menerapkan pola desain CQRS dalam aplikasi Anda, jika Anda memisahkan Perintah dari Kueri. Ini membawa Anda ke arah mendapatkan semua manfaat CQRS yang disebutkan dalam artikel.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

clemensv picture clemensv  ·  10Komentar

melzayet picture melzayet  ·  4Komentar

mikepfeiffer picture mikepfeiffer  ·  10Komentar

CloudLassoUK picture CloudLassoUK  ·  7Komentar

jtuliani picture jtuliani  ·  6Komentar