Natrafiłem na projekt Event Sourcing i chciałbym użyć go w aplikacji, w której potrzebny jest klient REST (a dokładniej RESTful). Jednak nie udało mi się połączyć ich ze sobą, ponieważ REST jest dość podobny do CRUD, a pozyskiwanie zdarzeń jest oparte na zadaniach. Zastanawiałem się, jak zaprojektować tworzenie poleceń na podstawie żądań do serwera REST. Rozważ ten przykład:
Za pomocą REST możesz nadać nowy zasób nazwie File. W jednym żądaniu możesz wysłać nową nazwę pliku, możesz zmienić folder nadrzędny i / lub zmienić właściciela pliku i tak dalej.
Jak zbudować serwer, aby móc korzystać ze źródeł zdarzeń. Myślałem o tych możliwościach:
Ustal na serwerze, które pola zostały zmienione i stworzyć odpowiednie polecenia (
RenameFileCommand
,MoveFileCommand
,ChangeOwnerCommand
, ...) i wysyła je indywidualnie. Jednak w tej konfiguracji każde z poleceń może zawieść, pozostawiając innych poza transakcją, a tym samym poza „atomową” zmianą zasobu.Wysyłamy tylko jedno polecenie (
UpdateFileCommand
) oraz obsługi poleceń, a dokładniej w agregacie, określić, które pola zostały zmienione i wysyłanie pojedynczych zdarzeń zamiast (FileRenamedEvent
,FileMovedEvent
,OwnerChangedEvent
, ...)Ten w ogóle mi się nie podoba: w żądaniu do serwera w nagłówkach określam, której komendy użyć, ponieważ interfejs użytkownika jest nadal oparty na zadaniach (ale komunikacja odbywa się za pośrednictwem REST). Nie powiedzie się to jednak przy żadnym innym użyciu komunikacji REST (np. W aplikacjach zewnętrznych), ponieważ nie są zobowiązani do zmiany tylko jednego pola w jednym żądaniu. Wnoszę też całkiem spore sprzężenie z interfejsem użytkownika, REST i ES.
Który wolisz, czy jest na to lepszy sposób?
Uwaga dodatkowa: aplikacja napisana w Javie i Axon Framework do pozyskiwania zdarzeń.
źródło
Odpowiedzi:
Myślę, że możesz mieć tutaj niedopasowanie procesu użytkownika do procesu implementacji.
Po pierwsze: czy użytkownik szczerze chce wykonać wiele zmian w pliku jednocześnie? Zmiana nazwy (która może, ale nie musi obejmować zmiany ścieżki?), Zmiana własności i być może zmiana zawartości pliku (ze względu na argument) wydają się być osobnymi działaniami.
Weźmy przypadek, w którym odpowiedź brzmi „tak” - Twoi użytkownicy naprawdę chcą wprowadzić te zmiany jednocześnie.
W takim razie, ja zdecydowanie odradzam dowolnej implementacji, który wysyła wiele zdarzeń -
RenameFileCommand
,MoveFileCommand
,ChangeOwnerCommand
- reprezentować ten pojedynczy intencje użytkownika.Dlaczego? Ponieważ zdarzenia mogą się nie powieść. Być może jest to niezwykle rzadkie, ale użytkownik przesłał operację, która wyglądała na atomową - jeśli jedno z poniższych zdarzeń zakończy się niepowodzeniem, stan aplikacji jest teraz nieprawidłowy.
Zapraszasz również zagrożenia rasowe do zasobu, który jest wyraźnie współdzielony przez każdą z osób zajmujących się wydarzeniami. Będziesz musiał napisać „ChangeOwnerCommand” w taki sposób, aby nazwa pliku i ścieżka pliku nie miały znaczenia, ponieważ mogą być nieaktualne do czasu otrzymania polecenia.
Wdrażając niespieszny system sterowany zdarzeniami z przenoszeniem i zmianą nazw plików, wolę zapewnić spójność, używając czegoś takiego jak system eTag - upewnij się, że edytowany zasób jest wersją ostatnio pobraną przez użytkownika, i jeśli nie, od tego czasu został zmodyfikowany. Ale jeśli wysyłasz wiele poleceń dla tej operacji dla pojedynczego użytkownika, będziesz musiał zwiększyć swoją wersję zasobu po każdym poleceniu - więc nie masz sposobu, aby wiedzieć, że zasób, który użytkownik edytuje, jest naprawdę tą samą wersją, co zasób, który ostatnio czytał .
Rozumiem przez to - co jeśli ktoś inny wykonuje inną operację na pliku w tym samym czasie. 6 poleceń można ułożyć w stos w dowolnej kolejności. Gdybyśmy mieli tylko 2 polecenia atomowe, wcześniejsze polecenie mogłoby się powieść, a późniejsze polecenie mogło się nie powieść „zasób został zmodyfikowany od ostatniego pobrania”. Ale nie ma ochrony przed tym, gdy polecenia nie są atomowe, więc naruszona jest spójność systemu.
Co ciekawe, w REST nastąpił ruch w kierunku architektury opartej na zdarzeniach, zwanej „Odpoczynek bez PUT”, zalecanej przez radar technologii Thoughtworks, styczeń 2015 . Istnieje tutaj znacznie dłuższy blog o odpoczynku bez PUT .
Zasadniczo chodzi o to, że POST, PUT, DELETE i GET są odpowiednie dla małych aplikacji, ale gdy musisz zacząć zakładać, jak interpretować wstawianie i wysyłanie i usuwanie może być interpretowane na drugim końcu, możesz wprowadzić sprzężenie. (np. „kiedy USUWAM zasób powiązany z moim kontem bankowym, konto powinno zostać zamknięte”). Proponowane rozwiązanie polega na traktowaniu REST w sposób bardziej pozyskany od zdarzenia. tj. POST zamiar użytkownika jako pojedynczy zasób zdarzenia.
Drugi przypadek jest prostszy. Jeśli użytkownicy nie chcą wykonywać wszystkich tych operacji jednocześnie, nie pozwól im. POST zdarzenie dla każdego zamiaru użytkownika. Teraz możesz korzystać z wersji etag na swoich zasobach.
Podobnie jak w przypadku innych aplikacji, które używają zupełnie innego interfejsu API niż zasoby. To pachnie kłopotami. Czy możesz zbudować fasadę starego interfejsu API na szczycie interfejsu API RESTful i skierować je na fasadę? tj. udostępnić usługę, która wykonuje wiele aktualizacji pliku w sekwencji za pośrednictwem serwera REST?
Jeśli nie zbudujesz interfejsu RESTful na starym rozwiązaniu, ani nie zbudujesz fasady starego interfejsu na rozwiązaniu REST i nie będziesz próbował utrzymać obu interfejsów API wskazujących na udostępniony zasób danych, napotkasz poważne problemy.
źródło
Właśnie teraz natknąłem się na następujący artykuł, który zachęca do podania nazw poleceń w żądaniu do serwera w nagłówku Content-Type (przy zachowaniu 5 poziomów typu mediów).
W artykule wspominają, że styl RPC jest zły dla REST i sugerują rozszerzenie Content-Type w celu określenia nazwy polecenia:
Artykuł znajduje się tutaj: http://www.infoq.com/articles/rest-api-on-cqrs
Możesz przeczytać więcej o 5 poziomach rodzaju mediów tutaj: http://byterot.blogspot.co.uk/2012/12/5-levels-of-media-type-rest-csds.html
Chociaż udostępniają zdarzenia domeny interfejsowi API REST, co uważam za złą praktykę, podoba mi się to rozwiązanie, ponieważ nie tworzy ono nowego „protokołu” wyłącznie dla CQRS, czy to wysyłania nazw poleceń w treści, czy w dodatkowej postaci nagłówek i pozostaje wierny zasadom RESTful.
źródło