Jak opisać zmianę architektoniczną, która celowo łamie standardy REST?

37

Proponuję zmiany w bardzo źle zaprojektowanym projekcie oprogramowania, który ma wiele problemów. Na wysokim poziomie projekt wykorzystuje Angular na interfejsie i zużywa różne interfejsy API REST; co jest świetne (nie widzę potrzeby zmiany naszej technologii ani narzędzi). Problem polega na tym, że podstawa kodu jest nieproporcjonalnie większa w interfejsie użytkownika niż interfejsy API po stronie serwera. Znaczna część logiki biznesowej znajduje się w interfejsie użytkownika, przy czym interfejsy API REST są prostymi interfejsami bazy danych CRUD z warstwą interfejsu użytkownika.

Na przykład POST do klienta utworzy rekord klienta, a PUT zmodyfikuje tego klienta. Niewiele więcej i niewiele mniej. Nasza logika biznesowa jest jednak bardziej wymagająca. Ogólny proces tworzenia klienta robi znacznie więcej niż wstawianie 1 rekordu bazy danych. Dostarczy dane w innych niezbędnych tabelach, wykona pewne walidacje i obliczenia itp. Wolałbym wykonać pojedyncze wywołanie POST / PUT, które zawiera wszystkie te zachowania, zmniejszając obciążenie klienta konsumującego.

Dlatego uważam, że ta nadrzędna aranżacja powinna znajdować się na serwerze (gdzie mamy pełną kontrolę, dzienniki itp.), A nie w interfejsie użytkownika, ale jednym kontrargumentem jest to, że takie podejście nie byłoby już RESTful. Nie jestem więc pewien, jak najlepiej opisać to podejście, gdy zalecam kontynuowanie istniejącego stosu technologii, ale zaimplementuj podstawowe zmiany w lokalizacjach, do których należy kod.

Ben Harrison
źródło
44
Ograniczenie interfejsu API do CRUD w celu uczynienia go „RESTful” jest kiepskim kompromisem.
Robert Harvey
38
@EsbenSkovPedersen: Best Friend Forever?
Robert Harvey
5
Zamiast martwić się o to, czy Twoja usługa jest zgodna z REST (iirc, prawie żadna nie), martwiłbym się bardziej o zgodność ze specyfikacją HTTP . Większość interfejsów API, z którymi pracowałem, również nie jest zgodna ze specyfikacją, ale jest to bardziej osiągalna i wartościowa bramka imo.
aaaaaa
7
@ aaaaaa, powodem, dla którego prawie żadne usługi nie są zgodne z REST, jest to, że nikt nie może zdecydować, co to jest REST. Jedyny punkt porozumienia, jaki znalazłem, to „wszyscy robią to źle”.
Mark
16
- „Jak opisać zmianę architektoniczną, która celowo łamie standardy REST?” - disRESTpect . ( Przepraszam za nieprofesjonalny komentarz, był silniejszy ode mnie. )
luk32

Odpowiedzi:

49

Nie jestem pewien, jak najlepiej opisać to podejście, gdy moim zaleceniem jest kontynuowanie istniejącego stosu technologii, ale zaimplementuj podstawowe zmiany w lokalizacjach, do których należy kod.

Service oriented architecture.

Proponujesz przeprojektować system, aby reguły biznesowe i dane znajdowały się w tym samym miejscu. To faktycznie jest definicja usługi ; patrz wykład Udi Dahana na temat Finding Service Granice .

Pasek boczny: jak zauważył Eric, nie ma to nic wspólnego z „REST”. Nie ma absolutnie żadnego powodu, dla którego nie można umieścić interfejsu API REST (czyli interfejsu API, który spełnia ograniczenia stylu architektonicznego REST ) przed usługą. Ale może to nie być oczywiste dla osób, które rozumieją REST jako mapowanie operacji bazy danych na metody HTTP.

Warto inwestować w zmianę zrozumienia REST przez odbiorców.

VoiceOfUnreason
źródło
32
Nie warto też w ogóle inwestować w REST. Jeśli przeczytasz rozprawę Roya Fieldinga (lub Jak wyjaśniłem REST mojej żonie ), prawdziwym celem REST jest zapewnienie kanonicznej reprezentacji zasobów w Internecie, tak aby różne maszyny w Internecie miały standardowy sposób manipulowania tymi zasobami . Aplikacja prywatna może nawet nie potrzebować tej możliwości.
Robert Harvey
29

REST nie jest CRUD. Ten „kontrargument” opiera się na zasadniczo błędnym zrozumieniu tego, czym jest REST. Nie widziałem w poście nic, co wskazywałoby, że zmiana spowodowałaby, że interfejs API byłby mniej lub bardziej RESTful.

Eric Stein
źródło
6
Cóż, nie, nie jest to idealne mapowanie do CRUD, ale chodzi, mówi i śpiewa bardzo podobnie do CRUD, przynajmniej w sposób, w jaki większość ludzi to interpretuje.
Robert Harvey
11
@RobertHarvey Myślę, że to (błędne) zrozumienie jest tutaj problemem.
JimmyJames
4
@JimmyJames: To wszechobecne nieporozumienie. Istnieje silna potrzeba, aby rzeczy „uspokajały”, gdy większość ludzi nawet nie rozumie, jakie są korzyści i w jaki sposób te korzyści miałyby do nich zastosowanie.
Robert Harvey
4
@RobertHarvey Myślę, że mówisz, że jeśli zrobienie tego w niewłaściwy sposób to REST, to REST nie powinien być celem. OK, ale tak, jak to widzę, nazywanie tego „nie REST” to bzdury, a ja jestem wielkim zwolennikiem nazywania bzdur na bzdurach. Słowa potrzebują powszechnie rozumianego znaczenia, aby były użyteczne.
JimmyJames
5
@RobertHarvey Przyznaję, ale tak się nie stanie, dopóki będzie wystarczająco dużo osób, które chętnie skorygują te niewłaściwe użycie tego terminu. Nie jestem gotowy rzucić ręcznika.
JimmyJames
24

Jeszcze jedną rzeczą, o której należy pamiętać ... Nie sprawdzanie poprawności strony serwera reguł biznesowych oznacza, że ​​domyślnie ufasz wszystkim, co się pojawi, np. Przez żądanie POST, jest prawidłowe.

Oznacza to, że na przykład, podczas gdy twoja aplikacja kątowa może sprawdzić, czy klient ma prawidłowy przedział wiekowy i zapewnia, że ​​uprawnieni użytkownicy otrzymają prawidłową informację zwrotną, każdy, kto zna adres URL twojego interfejsu API, może wykonać żądanie POST zawierające niektóre nieuzasadnione wartości, które mogłyby nie będzie już sprawdzany.

Tak więc moją propozycją byłoby przeniesienie reguł biznesowych do interfejsu API, pozwolenie na sprawdzenie poprawności danych wejściowych i zwrócenie odpowiednich błędów (a może tylko kodów wskazujących, co poszło nie tak) w treści odpowiedzi. Kody te można następnie wykorzystać w aplikacji front-end do wskazania, co poszło nie tak.

mrsmn
źródło
5
To zdecydowanie najbardziej użyteczna odpowiedź tutaj: API to powierzchnia ataku, a nie dane wejściowe do klienta. Każde żądanie API może być sfałszowane. Zatem wszystko, co można zrobić przy użyciu czystego interfejsu API, jest tym, co może zrobić bez talentu, złośliwy dzieciak. Oprogramowanie klienckie może być używane w celu zapewnienia lepszej wygody użytkownika, ale to serwer musi egzekwować reguły.
cmaster
10

Aby dodać do innych dobrych odpowiedzi tutaj:

Interfejs, REST lub inny, nie powinien być ograniczony w oparciu o pewne założenia dotyczące szczegółów implementacji. Jest to całkowicie sprzeczne z pojęciem usług jako warstwy abstrakcji.

Jedną z głównych zalet korzystania z usług jest to, że szczegóły implementacji można zmieniać bez potrzeby wykonywania jakichkolwiek czynności przez klientów. Z tego, co opisałeś, wygląda na to, że nie ma prawdziwej warstwy abstrakcji. Szczegóły implementacji zostały ujawnione przez HTTP. Nic w REST nie mówi, że jest to konieczne, pomocne lub pożądane. W rzeczywistości wydaje mi się, że mógłbym argumentować pewne części definicji REST, aby oznaczać, że jest to w rzeczywistości implementacja bez RESTful.

Sugerujesz, jak zaprojektować odpowiednią warstwę usług. Jeśli ktoś mówi ci, że nie możesz tego zrobić, ponieważ nie jest to ODPOCZYNEK, to niefortunne. Możesz być pewien, że ktoś, kto ci powie, że niewiele wie nic o REST.

W oparciu o twoje pytanie masz zasób o nazwie klient. Wszystko i wszystko, co jest wymagane do utworzenia prawidłowego zasobu klienta, może i powinno być obsługiwane w POSTzasobie podstawowym klienta (lub alternatywnie / opcjonalnie w PUT do określonego zasobu klienta, jeśli nie istnieje). REST nie mówi nic o tym, ile rekordy bazy danych, które należy utworzyć dla danego połączenia. Jak zauważył Colin Young, w ogóle nie musi istnieć baza danych, nie ma znaczenia, w jaki sposób usługi są wdrażane z perspektywy REST.

JimmyJames
źródło
3
REST nie mówi nic o rekordach bazy danych, nie mówiąc już o ich liczbie. Mógłbym utworzyć usługę REST, która kontrolowałaby zawór wodny i ujawniała zawór wodny, zasoby wodne i zasoby na poziomie zbiornika. Ty mógłby argumentować obiektów fizycznych sami są „bazy danych” ale że jest rozciąganie co nieco.
Colin Young,
@ColinYoung Tak, dziękuję za pomoc w wyjaśnieniu.
JimmyJames
3

Jest tu kilka dobrych odpowiedzi, ale nie jestem pewien, czy pomogą ci przekonać współpracowników. Jak wielu zauważyło, to, co sugerujesz, nie jest odejściem od RESTful design i myślę, że jest to kluczem do włączenia ich w twoją propozycję.

REST nie polega na upewnieniu się, że interfejs API pozwala tylko na przechowywanie i pobieranie danych. Raczej dotyczy działań modelowania jako zasobów. Twój interfejs API powinien umożliwiać podejmowanie działań (w końcu jest to interfejs programowania aplikacji ). Pytanie brzmi, jak modelować te działania.

Zamiast wymyślić termin, przykłady są prawdopodobnie najlepszym sposobem na wyjaśnienie tego swoim współpracownikom . W ten sposób możesz pokazać, jak oni to robią teraz, jakie problemy to powoduje, rozwiązanie, które rozwiązuje problem i jak nadal pozostaje ODPOWIEDZIALNY.

Spójrzmy na obiekt klienta.

Problem:

Interfejsy użytkownika POST klienta, ale kolejne tabele nie zostały jeszcze zaktualizowane. Co się stanie, jeśli jedno z kolejnych wywołań nie powiedzie się z powodu błędu w kodzie interfejsu użytkownika (lub nieprawidłowo działającej wtyczki przeglądarki itp.)? Teraz Twoje dane są niespójne. Może to być nawet stan, który psuje inne części interfejsu API lub interfejsu użytkownika, nie wspominając już o tym, że jest po prostu nieprawidłowy. Jak się odzyskujesz? Będziesz musiał przetestować każdy możliwy stan, aby upewnić się, że to niczego nie zepsuje, ale trudno byłoby wiedzieć, co jest możliwe.

Rozwiązanie:

Utwórz punkt końcowy interfejsu API, aby utworzyć klientów. Wiesz, że nie chcesz mieć punktu końcowego „/ customer / create”, a nawet „/ create-customer”, ponieważ create jest czasownikiem i naruszy REST. Więc dopracuj to. „/ tworzenie-klientów” może działać. Teraz, kiedy opublikujesz swój obiekt CustomerCreation, wyśle ​​on wszystkie potrzebne pola, aby klient mógł zostać w pełni utworzony. Punkt końcowy zapewni, że dane są kompletne i poprawne (zwracając wartość 400 lub coś, jeśli nie powiedzie się sprawdzanie poprawności) i może na przykład zachować wszystko w ramach jednej transakcji db.

Jeśli potrzebujesz również punktu końcowego do GET / obiektów klientów, to w porządku. Możesz mieć oba. Sztuką jest tworzenie punktów końcowych, które spełniają potrzeby konsumentów.

Zalety:

  1. Gwarantujesz, że nie skończy się zły stan
  2. U deweloperów interfejsu użytkownika jest to łatwiejsze, jeśli nie muszą oni „znać” porządkowania żądań, problemów związanych z weryfikacją itp
  3. Interfejs API nie jest tak gadatliwy, co zmniejsza opóźnienie żądań sieciowych
  4. Łatwiej jest testować i konceptualizować scenariusze (brakujące / zniekształcone fragmenty danych z interfejsu użytkownika nie są rozłożone na żądania, z których niektóre mogą się nie powieść)
  5. Umożliwia lepszą enkapsulację logiki biznesowej
  6. Zasadniczo ułatwia bezpieczeństwo (ponieważ użytkownicy mogą modyfikować logikę biznesową i aranżacyjną w interfejsie użytkownika)
  7. Prawdopodobnie ograniczy duplikację logiki (bardziej prawdopodobne, że będziesz mieć ponad 2 konsumentów interfejsu API niż 2+ interfejsy API, które dają dostęp do tych samych danych)
  8. Nadal 100% RESTful

Niedogodności:

  1. Jest to potencjalnie więcej pracy dla programistów zaplecza (ale może nie być na dłuższą metę)

Ludziom może być trudno zrozumieć ten paradygmat i co w nim dobrego, jeśli go nie wypróbowali. Mamy nadzieję, że możesz pomóc im zobaczyć, korzystając z przykładu z własnego kodu.

Moje własne doświadczenie jest takie, że gdy deweloperzy w moim zespole zaczęli wdrażać tę strategię, prawie natychmiast dostrzegli korzyści.

Dalsze badanie:

Ten artykuł z thinktworks naprawdę pomógł mi zrozumieć modelowanie działań jako obiektów na praktycznych przykładach: https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling

Sugerowałbym również przeczytanie o CQRS i zaopatrzeniu w zdarzenia, ponieważ dotyczą one właśnie tego rodzaju rzeczy (tj. Oddzielenie interfejsu API od rzeczywistej logiki trwałości). Nie wiem, jak chętni byliby twoi współpracownicy, aby czytać tego rodzaju rzeczy, ale może to dać ci większą jasność i pomóc ci to wyjaśnić.

Desek
źródło
ponieważ tworzenie jest czasownikiem i naruszyłoby REST ” - całkowicie poprawne. Innymi słowy, byłoby 47.258.346. podejściem do uruchomienia „ RPC over REST ”. Które to przypisałbym co najmniej „nienaturalnemu”, ponieważ niewłaściwie wykorzystuje i błędnie reprezentuje podejścia RESTful (mają swoje przypadki użycia, ale RPC nie jest jednym z nich), a także bywa nieefektywne.
JensG