Sumber acara dan REST


17

Saya menemukan desain Event Sourcing dan saya ingin menggunakan dalam aplikasi di mana klien REST diperlukan (RESTful to be exact). Namun saya gagal menghubungkan ini bersama-sama karena REST cukup mirip CRUD dan sumber acara berbasis tugas. Saya bertanya-tanya bagaimana Anda bisa merancang pembuatan perintah berdasarkan permintaan ke server REST. Pertimbangkan contoh ini:

Dengan REST Anda dapat menempatkan negara baru ke sumber daya yang disebut File. Dalam satu permintaan Anda dapat mengirim nama file baru, Anda dapat mengubah folder induk dan / atau mengubah pemilik file dan sebagainya.

Bagaimana membangun server sehingga saya bisa menggunakan sumber acara. Saya sedang memikirkan kemungkinan-kemungkinan ini:

  1. Tentukan pada server yang bidang diubah dan membuat perintah yang sesuai ( RenameFileCommand, MoveFileCommand, ChangeOwnerCommand, ...) dan pengiriman ini secara individual. Namun dalam pengaturan ini, masing-masing perintah dapat gagal membiarkan orang lain keluar dari transaksi dan dengan demikian keluar dari "atom" perubahan ke sumber daya.

  2. Dispatch hanya satu perintah ( UpdateFileCommand) dan dalam penangan perintah, lebih tepatnya di agregat, menentukan bidang yang berubah dan mengirim peristiwa individu, bukan ( FileRenamedEvent, FileMovedEvent, OwnerChangedEvent, ...)

  3. Yang ini saya tidak suka sama sekali: Dalam permintaan ke server saya akan menentukan dalam header yang perintah untuk digunakan, karena UI masih berbasis tugas (tetapi komunikasi dilakukan melalui REST). Namun itu akan gagal dalam penggunaan komunikasi REST lainnya (misalnya dalam aplikasi eksternal) karena mereka tidak terikat untuk mengubah hanya satu bidang dalam satu permintaan. Saya juga membawa kopling yang cukup besar ke backend berbasis UI, REST, dan ES.

Yang mana yang Anda inginkan atau ada cara yang lebih baik untuk menangani ini?

Catatan: aplikasi ditulis dalam Java dan Axon Framework untuk event-sourcing.


Tentu bukan yang ke-3. Saya akan melakukan yang pertama, tetapi saya harus memikirkannya.
inf3rno

Apakah Anda mempertanyakan apakah satu permintaan HTTP dapat menghasilkan banyak Perintah? Apakah saya mengerti dengan baik? Menurut opini saya. seharusnya bisa melakukannya, tetapi saya belum membaca tentang DDD untuk sementara waktu, jadi saya harus memeriksa kode sampel tentang bagaimana menerapkan ini.
inf3rno

Saya menemukan contoh, tapi itu CRUD. github.com/szjani/predaddy-issuetracker-sample/blob/3.0/src/hu/... Saya akan bertanya kepada penulis apa pendapatnya, dia tahu lebih banyak tentang DDD daripada saya.
inf3rno

1
Milik saya adalah bahwa Anda harus menggunakan banyak perintah dalam "unit kerja" jika Anda ingin konsistensi langsung. Jika Anda berbicara tentang konsistensi akhirnya maka pertanyaan itu tidak masuk akal bagi saya. Solusi lain yang mungkin untuk mengirim CompositeCommand yang dapat berisi Perintah yang ingin Anda jalankan atom. Ini dapat berupa koleksi sederhana, satu-satunya hal yang penting bahwa bus dapat menanganinya dengan benar.
inf3rno

1
Menurutnya Anda harus mencoba mencapai hubungan 1: 1 antara perintah dan permintaan HTTP. Jika Anda tidak dapat melakukannya (itu adalah bau busuk), maka Anda harus menggunakan bulk (saya menyebutnya komposit) untuk membuatnya menjadi atom.
inf3rno

Jawaban:


11

Saya pikir Anda mungkin memiliki proses pengguna untuk implementasi ketidakcocokan di sini.

Pertama: apakah pengguna secara jujur ​​ingin melakukan beberapa perubahan pada file secara bersamaan? Mengubah nama (yang mungkin atau mungkin tidak termasuk perubahan jalur?), Perubahan kepemilikan, dan mungkin perubahan konten file (demi argumen) tampak seperti tindakan terpisah.

Mari kita bahas jawabannya "ya" - pengguna Anda benar-benar ingin melakukan perubahan ini secara bersamaan.

Dalam hal ini, saya akan sangat menyarankan terhadap setiap implementasi yang mengirimkan beberapa peristiwa - RenameFileCommand, MoveFileCommand, ChangeOwnerCommand- untuk mewakili ini tunggal maksud pengguna.

Mengapa? Karena kejadian bisa gagal. Mungkin ini sangat jarang, tetapi pengguna Anda mengirimkan operasi yang terlihat seperti atom - jika salah satu dari peristiwa hilir gagal, maka status aplikasi Anda sekarang tidak valid.

Anda juga mengundang bahaya ras pada sumber daya yang jelas dibagi antara masing-masing penangan acara. Anda perlu menulis "ChangeOwnerCommand" sedemikian rupa sehingga nama file dan path file tidak menjadi masalah, karena mereka mungkin ketinggalan zaman pada saat perintah diterima.

Saat menerapkan sistem diam non-event yang digerakkan dengan memindahkan dan mengganti nama file, saya lebih suka memastikan konsistensi dengan menggunakan sesuatu seperti sistem eTag - memastikan bahwa versi sumber daya yang sedang diedit adalah versi yang terakhir diambil pengguna, dan gagal jika itu telah dimodifikasi sejak saat itu. Tetapi jika Anda mengirim banyak perintah untuk operasi satu pengguna ini, Anda perlu menambah versi sumber daya Anda setelah setiap perintah - jadi Anda tidak memiliki cara untuk mengetahui bahwa sumber daya yang diedit pengguna benar-benar adalah versi yang sama dengan sumber daya yang terakhir mereka baca .

Yang saya maksud dengan itu adalah - bagaimana jika orang lain melakukan operasi lain pada file pada waktu yang hampir bersamaan. 6 perintah bisa menumpuk dalam urutan apa pun. Jika kita hanya memiliki 2 perintah atom, perintah sebelumnya bisa berhasil dan perintah selanjutnya bisa gagal "sumber daya telah dimodifikasi sejak terakhir diambil". Tetapi tidak ada perlindungan terhadap ini ketika perintah tidak atom, sehingga konsistensi sistem dilanggar.

Menariknya ada gerakan menuju sesuatu seperti arsitektur berbasis acara di REST, yang disebut "Istirahat tanpa PUT", yang direkomendasikan dalam radar teknologi Thoughtworks, Jan 2015 . Ada blog yang jauh lebih lama tentang Istirahat tanpa PUT di sini .

Intinya, idenya adalah POST, PUT, DELETE, dan GET baik-baik saja untuk aplikasi kecil, tetapi ketika Anda harus mulai dengan asumsi bagaimana menempatkan dan memposting dan menghapus mungkin ditafsirkan di ujung lain, Anda memperkenalkan kopling. (mis. "Ketika saya MENGHAPUS sumber daya yang terkait dengan rekening bank saya, akun itu harus ditutup") Dan solusi yang diusulkan adalah memperlakukan REST dengan cara yang lebih bersumber dari Peristiwa. yaitu Mari POST niat pengguna sebagai sumber daya peristiwa tunggal.

Kasus lainnya lebih sederhana. Jika pengguna Anda tidak ingin melakukan semua operasi secara bersamaan, jangan biarkan mereka. POST acara untuk setiap maksud pengguna. Sekarang Anda dapat menggunakan versi etag pada sumber daya Anda.

Adapun aplikasi lain yang menggunakan API sangat berbeda untuk sumber daya Anda. Baunya seperti masalah. Bisakah Anda membuat fasad API lama di atas API RESTful dan mengarahkannya ke fasad? yaitu mengekspos layanan yang melakukan beberapa pembaruan ke file secara berurutan melalui server REST?

Jika Anda tidak membangun antarmuka RESTful di atas solusi lama, atau membangun fasad antarmuka lama di atas solusi REST, dan berupaya mempertahankan kedua API yang mengarah pada sumber data bersama, Anda akan mengalami sakit kepala besar.


Saya dapat melihat ketidaksesuaian, oleh REST pada prinsipnya adalah mungkin untuk memperbarui keadaan beberapa bidang dengan PUTting negara baru ke sumber daya (oleh beberapa representasi). Beginilah cara REST bekerja berdasarkan definisi - transfer status representasional, sisi buruknya adalah bahwa maksud pengguna hilang dalam proses. Selain itu, REST hampir merupakan standar untuk API eksternal. Saya hanya ingin merancang aplikasi sehingga saya bisa menggunakan keduanya. Menghapus PUT seperti solusi karena ES. Dari apa yang saya lihat, satu perintah pembaruan yang memancarkan banyak peristiwa bisa dilakukan selama perintah dan penanganan peristiwa dalam satu transaksi.
Si rambut merah

Etag adalah sesuatu yang ingin saya lakukan. Juga tautan Anda ke "posting blog yang lebih panjang" sama dengan tautan pertama.
Si rambut merah

Saya akan kembali ke ini setelah beberapa pertimbangan dengan lebih banyak pemikiran kembali PUT. Tentu saja, menghapus PUT bukan hanya "solusi untuk ES". Saya telah memperbaiki tautan blog.
perfeksionis

4

Baru saja saya berlari ke artikel berikut, yang mendorong menentukan nama perintah dalam permintaan ke server di header Tipe-Konten (sambil mengikuti 5 tingkat jenis media).

Dalam artikel tersebut, mereka menyebutkan gaya RPC buruk untuk REST dan menyarankan untuk memperluas Tipe Konten untuk menentukan nama perintah:

Salah satu pendekatan umum adalah menggunakan sumber daya gaya RPC misalnya / api / InventoryItem / {id} / rename. Meskipun ini tampaknya menghilangkan kebutuhan untuk kata kerja yang arbitrer, itu bertentangan dengan presentasi berorientasi sumber daya REST. Kita perlu diingatkan bahwa sumber daya adalah kata benda dan kata kerja HTTP adalah kata kerja / tindakan dan pesan deskriptif diri (salah satu prinsip REST) ​​adalah kendaraan untuk menyampaikan sumbu informasi dan maksud lainnya. Sebenarnya perintah dalam payload dari pesan HTTP harus cukup untuk menyatakan tindakan sewenang-wenang. Namun, mengandalkan tubuh pesan memiliki masalah tersendiri karena tubuh biasanya disampaikan sebagai aliran dan penyangga tubuh secara keseluruhan sebelum mengidentifikasi tindakan tidak selalu mungkin atau bijaksana.

PUT /api/InventoryItem/4454c398-2fbb-4215-b986-fb7b54b62ac5 HTTP/1.1
Accept:application/json, text/plain, */*
Accept-Encoding:gzip,deflate,sdch
Content-Type:application/json;domain-model=RenameInventoryItemCommand`

Artikelnya ada di sini: http://www.infoq.com/articles/rest-api-on-cqrs

Anda dapat membaca lebih lanjut tentang 5 level jenis media di sini: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html


Meskipun mereka mengekspos peristiwa domain ke REST API, yang saya anggap praktik buruk, saya suka solusinya karena tidak membuat "protokol" baru hanya untuk CQRS, baik itu mengirim nama perintah di tubuh atau di ekstra sundulan, dan tetap setia pada prinsip RESTful.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.