Etkinlik kaynağı ve REST


17

Event Sourcing tasarımıyla karşılaştım ve bir REST istemcisinin gerekli olduğu bir uygulamada kullanmak istiyorum (kesin olmak RESTful). Ancak REST oldukça CRUD benzeri ve olay kaynak görev tabanlı olduğu için bunları birbirine bağlamak başarısız. REST sunucusuna yapılan isteklere göre komutların nasıl oluşturulacağını merak ediyordum. Bu örneği düşünün:

REST ile File adlı kaynağa yeni bir durum koyabilirsiniz. Bir istekte yeni dosya adı gönderebilirsiniz, üst klasörü ve / veya dosyanın sahibini vb. Değiştirebilirsiniz.

Nasıl olay kaynak kullanımı kullanabilirsiniz sunucu oluşturmak için. Bu olasılıkları düşünüyordum:

  1. Alanları değiştirildi hangi sunucuda belirleyin ve uygun komutları oluşturmak ( RenameFileCommand, MoveFileCommand, ChangeOwnerCommand, ...) ve tek tek bu da gönderir. Ancak bu kurulumda, komutun her biri diğerlerini işlem dışında bırakabilir ve böylece kaynaktaki "atomik" değişiklik dışında kalabilir.

  2. Sevk tek komutu ( UpdateFileCommand) ve komut işleyicisinde, daha doğrusu toplu olarak değiştirildi alanları belirlemek ve bunun yerine bireysel etkinlikleri göndermek ( FileRenamedEvent, FileMovedEvent, OwnerChangedEvent, ...)

  3. Bu hiç sevmiyorum: Sunucuya istekte, UI hala görev tabanlı (ancak iletişim REST üzerinden yapılır) çünkü hangi komutu kullanmak için başlıklarda belirtirdim. Bununla birlikte, REST iletişiminin başka herhangi bir kullanımında (örneğin harici uygulamalarda) başarısız olur, çünkü bir istekte yalnızca bir alanı değiştirmek zorunda değildirler. Ayrıca UI, REST ve ES tabanlı arka uca oldukça büyük bir bağlantı getiriyorum.

Hangisini tercih edersiniz veya bununla başa çıkmanın daha iyi bir yolu var mı?

Yan not: olay kaynak için Java ve Axon Framework yazılmış uygulama.


Kesinlikle 3. değil. İlkini yapardım, ama düşünmek zorundayım.
inf3rno

Tek bir HTTP isteğinin birden fazla Komut ile sonuçlanıp sonuçlanamayacağıyla ilgili sorularınız mı var? İyi anladım mı? Benim nacizane fikrime göre. Bunu yapabilmelidir, ancak DDD hakkında bir süredir okumadım, bu yüzden bunu nasıl uygulayacağımla ilgili örnek bir kodu kontrol etmeliyim.
inf3rno

Bir örnek buldum, ama bu CRUD. github.com/szjani/predaddy-issuetracker-sample/blob/3.0/src/hu/… Yazara fikrinin ne olduğunu soracağım, DDD hakkında benden daha çok şey biliyor.
inf3rno

1
Benimki, hemen tutarlılık istiyorsanız bir "iş birimi" nde birden fazla komut kullanmanız gerektiğidir. Nihai tutarlılıktan bahsediyorsanız, o zaman soru bana mantıklı gelmiyor. Atomik yürütmek istediğiniz Komutları içerebilen bir Kompozit Komut göndermek için başka bir olası çözüm. Basit bir koleksiyon olabilir, tek şey otobüsün düzgün bir şekilde başa çıkabileceği önemli.
inf3rno

1
Ona göre, komutlar ve HTTP istekleri arasında 1: 1 ilişki kurmaya çalışmalısınız. Bunu yapamazsanız (bu kötü bir koku), atom yapmak için toplu (kompozit olarak adlandırdım) kullanmalısınız.
inf3rno

Yanıtlar:


11

Burada uygulama uyumsuzluğu için bir kullanıcı süreciniz olabileceğini düşünüyorum.

Birincisi: Bir kullanıcı dürüstçe bir dosyada aynı anda birden fazla değişiklik yapmak isteyecek mi? Yeniden adlandırma (yol değişikliği içerebilir veya içermeyebilir mi?), Sahiplik değişikliği ve belki de dosya içeriğinin değiştirilmesi (tartışma amacıyla) ayrı eylemler gibi görünür.

Cevabın "evet" olduğu durumu ele alalım - kullanıcılarınız bu değişiklikleri aynı anda yapmak istiyor.

- Bu durumda, şiddetle birden fazla etkinlik gönderir herhangi uygulanmasına karşı öneriyoruz RenameFileCommand, MoveFileCommand, ChangeOwnerCommandbu temsil etmek - tek kullanıcı amacını.

Neden? Çünkü olaylar başarısız olabilir. Belki son derece nadirdir, ancak kullanıcı atomik görünen bir işlem sundu - aşağı akış olaylarından tek biri başarısız olursa, uygulama durumunuz şimdi geçersizdir.

Ayrıca, olay işleyicilerinin her biri arasında açıkça paylaşılan bir kaynak üzerindeki yarış tehlikelerini de davet ediyorsunuz. "ChangeOwnerCommand" komutunu, dosya adı ve dosya yolu önemli olmayacak şekilde yazmanız gerekir, çünkü komutun alındığı tarihe kadar güncel olmayabilirler.

Dosyaları taşıma ve yeniden adlandırma ile olay güdümlü dinlendirici bir sistem uygularken, eTag sistemi gibi bir şey kullanarak tutarlılığı sağlamayı tercih ediyorum - düzenlenen kaynağın sürümünün kullanıcının en son aldığı sürüm olduğundan emin olun ve eğer başarısız olursa o zamandan beri değiştirildi. Ancak bu tek kullanıcı işlemi için birden fazla komut gönderiyorsanız, her komuttan sonra kaynak sürümünüzü artırmanız gerekir; böylece kullanıcının düzenlediği kaynağın gerçekten son okuduğu kaynakla aynı sürüm olduğunu bilmenin hiçbir yolu yoktur .

Bununla ne demek istediğim - başka biri dosya üzerinde hemen hemen aynı anda başka bir işlem yaparsa. 6 komut herhangi bir sırada birikebilir. Sadece 2 atom komutumuz olsaydı, önceki komut başarılı olabilir ve sonraki komut başarısız olabilir "kaynak son alındığı zamandan beri değiştirildi". Ancak komutlar atomik olmadığında buna karşı bir koruma yoktur, bu nedenle sistem tutarlılığı ihlal edilir.

İlginçtir ki, REST'te , Thoughtworks teknoloji radarı, Ocak 2015'te önerilen " PUT'sız Dinlenme" adlı olay tabanlı mimari gibi bir şeye doğru bir hareket var . Burada PUT olmadan Rest hakkında çok daha uzun bir blog var .

Temel olarak, fikir POST, PUT, DELETE ve GET'in küçük uygulamalar için iyi olduğu, ancak koy ve gönder ve silme işleminin diğer uçta nasıl yorumlanabileceğini varsaymaya başlamanız gerektiğinde, bağlantıyı tanıtırsınız. (örneğin "banka hesabımla ilişkili kaynağı Sildiğimde, hesap kapatılmalıdır") Ve önerilen çözüm, REST'e daha fazla Olay kaynaklı bir şekilde davranmaktır. ie Kullanıcının tek bir olay kaynağı olarak POST yapmasını sağlar.

Diğer durum daha basit. Kullanıcılarınız tüm bu işlemleri aynı anda yapmak istemiyorsa, onlara izin vermeyin. Her kullanıcının amacı için bir etkinlik GÖNDERİN. Artık kaynaklarınızda etag sürüm oluşturmayı kullanabilirsiniz.

Kaynaklarınız için çok farklı bir API kullanan diğer uygulamalara gelince. Bu bela gibi kokuyor. RESTful API'nizin üzerine eski API'nin bir cephesini yapıp cepheye yönlendirebilir misiniz? örn., REST sunucusu üzerinden bir dosyaya birden çok güncelleme gerçekleştiren bir hizmeti ifşa etmek?

RESTful arayüzünü eski çözümün üzerine veya eski arayüzün REST çözümünün üzerine kurmazsanız ve her iki API'yi paylaşılan bir veri kaynağına işaret etmeye devam ederseniz, büyük baş ağrıları yaşayacaksınız.


Uyuşmazlığı görebiliyorum, ancak prensipte REST ile yeni durumu kaynağa (bazı temsillerle) PUTUT ederek birden çok alanın durumunu güncellemek mümkündür. REST'in tanımı gereği böyle çalışır - temsili durum aktarımı, aşağı taraf, kullanıcının niyeti sürecinde kaybolmasıdır. Ayrıca, REST harici API için neredeyse bir standarttır. Her ikisini de kullanabilmek için sadece uygulamayı tasarlamak istiyorum. ES nedeniyle PUT'un kaldırılması geçici çözüm gibidir. Gördüğüm kadarıyla, komut ve olay işleme bir işlemde olduğu sürece, birden fazla olay yayan bir güncelleme komutu yapılabilir.
redhead

Etag zaten yapmak istediğim bir şey. Ayrıca "daha uzun blog yazısı" bağlantınız ilk bağlantıyla aynıdır.
redhead

Ben daha fazla düşünce ile bazı düşünce sonra tekrar buna geri geleceğim. Kesinlikle, PUT'un kaldırılması sadece "ES için bir çözüm" değildir. Blog bağlantısını düzelttim.
mükemmeliyetçi

4

Şimdi, içerik türü üstbilgisinde sunucuya istekte komut adlarını belirtmeyi teşvik eden aşağıdaki makaleye girdim (5 ortam türü düzeyini izlerken).

Makalede, RPC stilinin REST için kötü olduğunu belirtiyorlar ve Komut adını belirtmek için Content-Type'ın genişletilmesini öneriyorlar:

Yaygın bir yaklaşım, RPC tarzı kaynakları kullanmaktır, örneğin / api / InventoryItem / {id} / rename. Bu görünüşte keyfi fiillere olan ihtiyacı ortadan kaldırsa da, REST'in kaynak odaklı sunumuna aykırıdır. Bir kaynağın bir isim olduğunu ve HTTP fiilinin fiil / eylem olduğunu ve kendini açıklayan mesajların (REST'in ilkelerinden biri) diğer bilgi eksenlerini ve amacı ileten araç olduğunu hatırlatmamız gerekir. Aslında, HTTP iletisinin yükündeki komut, herhangi bir keyfi eylemi ifade etmek için yeterli olmalıdır. Bununla birlikte, mesajın gövdesine güvenmek kendi başına problemlere sahiptir, çünkü beden genellikle bir akım olarak iletilir ve eylemi tanımlamadan önce bedeni bütünüyle arabelleğe almak her zaman mümkün ve akıllıca değildir.

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`

Makale burada: http://www.infoq.com/articles/rest-api-on-cqrs

5 medya türü düzeyi hakkında daha fazla bilgiyi buradan edinebilirsiniz: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html


Etki alanı olaylarını kötü bir uygulama olarak değerlendireceğim REST API'ye göstermelerine rağmen, yalnızca CQRS için yeni bir "protokol" oluşturmadığı için çözümü beğendim, ister vücutta ister ekstra olarak komut adları gönderiyor başlık ve RESTful ilkelerine sadık kalır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.