Dlaczego REST Api nie stosuje się do wzoru elewacji

9

Porównując strukturę REST [api] z modelem OO, widzę następujące podobieństwa:

Obie:

  • Są zorientowane na dane

    • REST = zasoby
    • OO = Przedmioty
  • Operacja surround wokół danych

    • REST = otaczaj VERBS (Get, Post, ...) wokół zasobów
    • OO = promuj działanie wokół obiektów przez enkapsulację

Jednak dobre praktyki OO nie zawsze stoją na apelach REST podczas próby zastosowania wzoru fasady, na przykład: w REST nie masz 1 kontrolera do obsługi wszystkich żądań ORAZ nie ukrywasz złożoności obiektów wewnętrznych.

Prosta relacja obiektowa między 2 pojęciami

Analogia między OO i REST

Przeciwnie, REST promuje publikowanie zasobów wszystkich relacji z zasobem i innymi w co najmniej dwóch formach:

  1. poprzez relacje hierarchii zasobów (kontakt o identyfikatorze 43 składa się z adresu 453): /api/contacts/43/addresses/453

  2. za pośrednictwem łączy w odpowiedzi json REST:

>> GET /api/contacts/43
<< HTTP Response {
   id: 43, ...
   addresses: [{
      id: 453, ...
   }],
   links: [{
      favoriteAddress: {
          id: 453
      }
   }]
}

Podstawowa złożoność ukryta przez obiekt A.

Wracając do OO, wzór projektowania elewacji szanuje Low Couplingmiędzy obiektem A i jego „ klientem objectB ”, a High Cohesiondla tego obiektu A i jego składu wewnętrznego obiektu ( objectC , objectD ). Z objectA interfejs, pozwala to deweloper, aby ograniczyć wpływ na objectB z objectA zmian wewnętrznych (w objectC i objectD ), tak długo, jak objectA API (operacji) są nadal przestrzegane.

W usłudze REST dane (zasoby), relacje (łącza) i zachowanie (czasowniki) są rozbite na różne elementy i dostępne w Internecie.

Korzystając z usługi REST, zawsze mam wpływ na zmiany kodu między moim klientem a serwerem: ponieważ mam High Couplingmiędzy swoimi Backbone.jsżądaniami i Low Cohesionmiędzy zasobami.

Nigdy nie zastanawiałem się, jak pozwolić Backbone.js javascript applicationsobie na odkryciezasobów i funkcji REST ” promowanych przez linki REST. Rozumiem, że WWW ma być obsługiwane przez wiele serwerów i że elementy OO musiały zostać rozbite, aby mogły być obsługiwane przez wiele hostów, ale w prostym scenariuszu, takim jak „zapisanie” strony pokazującej kontakt z jej adresami, Kończę z:

GET /api/contacts/43?embed=(addresses)
[save button pressed]
PUT /api/contacts/43
PUT /api/contacts/43/addresses/453

które skłoniły mnie do przeniesienia odpowiedzialności za transakcję atomową na transakcje atomowe na aplikacjach przeglądarki (ponieważ dwa zasoby można rozwiązać osobno).

Mając to na uwadze, jeśli nie mogę uprościć rozwoju (wzorce projektowania fasad nie mają zastosowania) i jeśli przyniosę więcej złożoności mojemu klientowi (obsługa transakcyjnego zapisu atomowego), to gdzie jest korzyść z ODPOWIEDZIALNOŚCI?

Alain
źródło
1
Pozwól mi zrozumieć Mówisz, że musisz zaktualizować Kontakt za pomocą „osadzonego” (połączonego) adresu połączonego za pomocą dwóch wywołań REST, jednego dla Kontaktu, a drugiego dla jego Adresu. Masz fasadę do obsługi aktualizacji kontaktów. Na czym polega problem z wprowadzaniem PUT /api/contacts/43kaskadowych aktualizacji wewnętrznych obiektów? Miałem wiele interfejsów API zaprojektowanych w ten sposób (główny adres URL odczytuje / tworzy / aktualizuje „całość”, a adresy URL aktualizują elementy). Tylko upewnij się, że nie aktualizujesz adresu, gdy nie są wymagane żadne zmiany (ze względu na wydajność).
Anthony Accioly
@AnthonyAccioly, zrozumiałeś poprawnie. Próbowałem wyjaśnić moje pytanie, dodając kilka zdjęć. Twoja sugestia jest dobra i do takiego wniosku doszłam: ręcznie sterując moim żądaniem i za pomocą osadzonego obiektu wyślij tylko jedno żądanie utrzymania atomu mojej aktualizacji. Mimo to: dlaczego wszystko w REST odsuwa mnie od cech OO lub egzekwowania (enkapsulacji, ...) poprzez spłaszczenie mojego modelu (co sugeruje wiele kontrolerów). Korzystanie z rozwiązania daje 1 aktualizację atomową. Nieużywanie Twojego rozwiązania niesie ze sobą nową odpowiedzialność programisty i brak reguł dotyczących interfejsu API, które by to uniemożliwiły.
Alain
Tylko uwaga: wspomniane „relacje hierarchii zasobów” są kwestią tego, w jaki sposób można zdecydować o kodowaniu informacji o relacjach w identyfikatorze (w tym przypadku adresie URL). Nie jestem pewien, czy to ujawnienie informacji jest częścią REST lub czymś, co promuje, tylko decyzją, którą podejmuje się sam, wymyślając adresy URL systemu. Jeśli uważasz inaczej, czy masz jakieś referencje Roy Fielding omawiające tę sprawę w ramach REST?
Thiago Silva,

Odpowiedzi:

7

Myślę, że obiekty są budowane poprawnie tylko wokół spójnych zachowań, a nie wokół danych. Sprowokuję i powiem, że dane są prawie nieistotne w świecie zorientowanym obiektowo. W rzeczywistości jest możliwe i czasami powszechne posiadanie obiektów, które nigdy nie zwracają danych, na przykład „zatapianie logów” lub obiektów, które nigdy nie zwracają danych, które są przekazywane, na przykład jeśli obliczają właściwości statystyczne.

Nie pomylmy PODS (które są niewiele więcej niż strukturami) i rzeczywistych obiektów, które mają zachowania (takie jak Contactsklasa w twoim przykładzie) 1 .

PODS to w zasadzie wygoda używana do rozmowy z repozytoriami i obiektami biznesowymi. Pozwalają na bezpieczny kod. Nie więcej nie mniej. Z drugiej strony obiekty biznesowe zapewniają konkretne zachowania , takie jak sprawdzanie poprawności danych, przechowywanie ich lub używanie ich do wykonywania obliczeń.

Zachowania są więc tym, czego używamy do mierzenia „kohezji” 2 , i łatwo jest zauważyć, że w twoim przykładzie obiektu istnieje pewna spójność, nawet jeśli pokazujesz tylko metody manipulowania kontaktami najwyższego poziomu, a nie metody manipulowania adresami.

Jeśli chodzi o REST, możesz zobaczyć usługi REST jako repozytoria danych. Duża różnica w stosunku do projektowania obiektowego polega na tym, że istnieje (prawie) tylko jeden wybór projektu: masz cztery podstawowe metody (więcej, jeśli HEADna przykład liczysz ) i oczywiście masz dużo swobody dzięki identyfikatorom URI, dzięki czemu możesz zrobić sprytne takie jak przekazywanie wielu identyfikatorów i odzyskiwanie większej struktury. Nie należy mylić przekazywanych danych z wykonywanymi operacjami . Spójność i sprzężenie dotyczą kodu, a nie danych .

Oczywiście usługi REST mają wysoką spójność (każdy sposób interakcji z zasobem znajduje się w tym samym miejscu) i niskie połączenie (każde repozytorium zasobów nie wymaga wiedzy o innych).

Podstawowym faktem pozostaje jednak, REST jest w zasadzie pojedynczym wzorcem repozytorium dla twoich danych. Ma to konsekwencje, ponieważ jest to paradygmat oparty na łatwej dostępności na wolnym nośniku, gdzie koszty „chattiness” są wysokie: klienci zazwyczaj chcą wykonać jak najmniej operacji, ale jednocześnie otrzymują tylko te dane, których potrzebują . Określa to, jak głęboko drzewo danych zamierzasz odesłać.

W (poprawnym) projektowaniu obiektowym każda nietrywialna aplikacja będzie wykonywać znacznie bardziej złożone operacje, na przykład poprzez kompozycję. Mogą istnieć metody wykonywania bardziej wyspecjalizowanych operacji na danych - tak musi być, ponieważ podczas gdy REST jest protokołem API, OOD służy do budowania aplikacji zorientowanych na użytkownika! Właśnie dlatego pomiar kohezji i sprzężenia ma podstawowe znaczenie w OOD, ale prawie nie ma znaczenia w REST.

Do tej pory powinno być oczywiste, że analiza projektu danych za pomocą koncepcji OO nie jest niezawodnym sposobem na zmierzenie go: przypomina porównywanie jabłek i pomarańczy!

W rzeczywistości okazuje się, że korzyści z RESTful są (głównie) te przedstawione powyżej: to dobry wzór dla prostych interfejsów API na wolnym medium. Jest bardzo buforowalny i możliwy do podzielenia. Ma drobnoziarnistą kontrolę nad spoceniem itp.

Mam nadzieję, że to odpowiada na twoje (dość wieloaspektowe) pytanie :-)


1 Ten problem jest częścią większego zestawu problemów znanych jako niedopasowanie impedancji obiektowo-relacyjnej . Zwolennicy ORM są na ogół w obozie, który bada podobieństwa między analizą danych a analizą zachowania, ale ORM są ostatnio krytykowane, ponieważ wydaje się, że tak naprawdę nie rozwiązują niedopasowania impedancji i są uważane za nieszczelne abstrakcje .

2 http://en.wikipedia.org/wiki/Coicity_(computer_science)

Sklivvz
źródło
Masz rację, miałam trudność z rozbiciem mojego pytania na wiele aspektów, aby ustalić konkretny punkt, ponieważ pytanie dotyczy „złej” konkluzji opartej na akumulacji tych aspektów. Spróbuję teraz odpowiedzieć na twoje uwagi w wielu komentarzach.
Alain
[tekst 1] Użyłem słowa „dane” do abstrakcji ze świata OO i REST. Jakiego słowa użyłbyś do wyodrębnienia właściwości w OO i struktury danych w REST?
Alain
@Alain „dane” jest w porządku, ale moim celem nie jest mylenie PODS i obiektów biznesowych. Kiedy mówimy o OOD, zwykle mówimy o drugim. Pierwsze są wygodą i niemal równie łatwo można pomyśleć o słowniku, strukturze lub krotce.
Sklivvz
Tak, zgadzam się, używam Backbone.js, gdzie zapisywanie modelu odbywa się za pomocą prostej struktury json. W tym miejscu tekst odzwierciedla moje rzeczywiste doświadczenie w kodowaniu.
Alain
[tekst 3] To dla mnie nowość. Myślałem, że spójność mierzy się za pomocą wielu metod wykorzystujących określoną relację ... Wolę twój sposób jej postrzegania.
Alain
1

Mając to na uwadze, jeśli nie mogę uprościć rozwoju (wzorce projektowania fasad nie mają zastosowania) i jeśli przyniosę więcej złożoności mojemu klientowi (obsługa transakcyjnego zapisu atomowego), to gdzie jest korzyść z ODPOWIEDZIALNOŚCI?

Odpowiedź na „gdzie jest korzyść z RESTful?” jest dokładnie przeanalizowane i wyjaśnione tutaj: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

Problem w tym pytaniu polega jednak na tym, że nie chodzi o charakterystykę REST i sposobu radzenia sobie z nimi, ale założenie, że adresy URL, które wymyśliłeś dla swojego przykładowego systemu, ma coś wspólnego z byciem RESTful. W końcu REST stwierdza, że ​​istnieją rzeczy zwane zasobami i należy podać identyfikator dla tych, do których należy się odwoływać, ale nie dyktuje to, że, powiedzmy, jednostki w twoim modelu ER powinny mieć 1-1 korespondencję z utworzonymi adresami URL (ani że adresy URL powinny kodować liczność relacji ER w modelu).

W przypadku kontaktów i adresów mógłbyś zdefiniować zasób, który wspólnie reprezentuje te informacje jako jedną jednostkę, nawet jeśli możesz chcieć wyodrębnić i zapisać te informacje, powiedzmy, w różnych relacyjnych tabelach DB, zawsze, gdy są PUT lub POSTed .

Thiago Silva
źródło
1

Dzieje się tak, ponieważ fasady są „kludge”; powinieneś spojrzeć na „abstrakcję API” i „tworzenie łańcuchów API”. Interfejs API to połączenie dwóch zestawów funkcji: We / Wy i zarządzania zasobami. Lokalnie we / wy jest w porządku, ale w architekturze rozproszonej (tj. Proxy, brama API, kolejka komunikatów itp.) We / wy jest współużytkowane, a zatem dane i funkcje zostają zduplikowane i uwikłane. Prowadzi to do architektonicznych problemów przekrojowych. To plaga WSZYSTKIE istniejące api.

Jedynym sposobem na rozwiązanie tego jest streszczenie funkcji we / wy interfejsu API dla modułu obsługi przed / po (takiego jak interfejs programu obsługi w Spring / Grails lub filtr w Railsach), dzięki czemu można go używać jako monady i współdzielić między instancjami i zewnętrznie obróbka. Dane do żądania / odpowiedzi również muszą być eksternalizowane w obiekcie, aby można je było również udostępniać i ładować ponownie.

http://www.slideshare.net/bobdobbes/api-abstraction-api-chaining

Orubel
źródło
0

Jeśli rozumiesz swoją usługę REST lub ogólnie dowolny interfejs API, podobnie jak dodatkowy interfejs udostępniony klientom, dzięki któremu mogą oni programować kontrolery przez niego, nagle staje się to łatwe. Ta usługa to nic innego jak dodatkowa warstwa nad twoją logiką biz.

Innymi słowy, nie musisz dzielić logiki dziwnej na wiele kontrolerów, jak na powyższym obrazku, a co ważniejsze, nie powinieneś. Struktury danych używane do wymiany danych nie muszą pasować do struktur danych używanych wewnętrznie, mogą być zupełnie inne.

Jest to stan techniki i powszechnie akceptowane, że umieszczanie dowolnej logiki biz w kodzie interfejsu użytkownika jest złym pomysłem. Ale każdy interfejs użytkownika jest tylko rodzajem interfejsu (I w interfejsie użytkownika) do sterowania logiką biz. W związku z tym wydaje się oczywiste, że złym pomysłem jest umieszczenie dowolnej logiki biz w warstwie usługi REST lub jakiejkolwiek innej warstwie API.

Pod względem koncepcyjnym nie ma aż tak dużej różnicy między interfejsem użytkownika a interfejsem API usługi.

JensG
źródło
Zgadzam się z pojęciem warstwy, ale co rozumiesz przez „zaprogramuj kontroler przez to”?
Alain
1
Chcę podkreślić fakt, że sam kontroler jest prawdziwą usługą. Interfejs owinięty wokół wszystkiego jest jedynie środkiem do osiągnięcia czegoś. Istnieje dowolny interfejs ułatwiający dostęp do opakowanej funkcjonalności w taki czy inny sposób. GUI robi to dla ludzi, interfejs API usługi jest używany przez klientów. Obie grupy docelowe chcą osiągnąć coś dzięki temu, co kryje się za interfejsem. Zgadzam się, że „program” może nie być najlepszym sformułowaniem na to, ale „sterowanie kontrolerami” również brzmi niezręcznie ;-)
JensG