2016-08-08 81 views
5

Załóżmy, że mamy szkoły z danymi zawierającymi imię i nazwisko oraz listę uczniów i studentów z danymi, w tym zapisami, do których są zapisani, oraz odniesieniem do ich szkoły. Na komputerze klienckim:Projektowanie interfejsu API: buforowanie "częściowych" obiektów zagnieżdżonych

  • Chciałbym wyświetlić ekran z informacjami o szkole, która zawiera listę wszystkich swoich uczniów po nazwisku.
  • Chciałbym wyświetlić ekran, który pokazuje informacje o uczniu, w tym nazwę szkoły i nazwy kursów, które biorą.
  • Chciałbym buforować te informacje, aby móc wyświetlać ten sam ekran bez oczekiwania na nowe pobranie. Powinienem móc wrócić ze szkoły do ​​ucznia i wrócić do szkoły bez ponownego pobierania nauki.
  • Chciałbym pokazać każdy ekran za pomocą tylko jednego pobrania. Przejście ze strony szkoły do ​​strony ucznia może przynieść osobne pobranie, ale powinienem pokazać szkołę z pełną listą nazwisk uczniów w jednym pobraniu.
  • Chciałbym uniknąć duplikowania danych, więc jeśli zmieni się nazwa szkoły, jedno pobranie aktualizacji szkoły spowoduje wyświetlenie poprawnej nazwy zarówno na stronie szkoły, jak i na stronach uczniów.

Czy istnieje dobry sposób, aby to wszystko zrobić, czy też niektóre z ograniczeń zostaną zniesione?

Pierwsze podejście byłoby mieć interfejs API, który robi coś takiego:

GET /school/1 

{ 
    id: 1, 
    name: "Jefferson High", 
    students: [ 
     { 
      id: 1 
      name: "Joel Kim" 
     }, 
     { 
      id: 2, 
      name: "Chris Green" 
     } 
     ... 
    ] 
} 


GET /student/1 

{ 
    id: 1, 
    name: "Joel Kim", 
    school: { 
     id: 1, 
     name: "Jefferson High" 
    } 
    courses: [ 
     { 
      id: 3 
      name: "Algebra 1" 
     }, 
     { 
      id: 5, 
      name: "World History" 
     } 
     ... 
    ] 
} 

Zaletą tego podejścia jest to, że dla każdego ekranu, możemy zrobić tylko jedno pobranie. Po stronie klienta moglibyśmy normalizować szkoły i uczniów, aby odwoływać się do siebie za pomocą identyfikatorów, a następnie przechowywać obiekty w różnych magazynach danych. Jednak obiekt student zagnieżdżony wewnątrz obiektu school nie jest obiektem pełnym - nie obejmuje kursów zagnieżdżonych ani odniesienia do szkoły. Podobnie obiekt school wewnątrz student nie ma listy wszystkich uczęszczających uczniów. Przechowywanie częściowych reprezentacji obiektów w magazynach danych może prowadzić do skomplikowanej logiki po stronie klienta.

Zamiast normalizować te obiekty, możemy przechowywać szkoły i uczniów z ich zagnieżdżonymi obiektami częściowymi. Oznacza to jednak duplikowanie danych - każdy uczeń w Jefferson High będzie miał nazwę szkoły zagnieżdżonej. Jeśli nazwa szkoły została zmieniona tuż przed pobraniem dla określonego ucznia, wyświetliliśmy nazwę szkoły dla tego ucznia, ale złe imię wszędzie, w tym na stronie "szczegóły szkoły".

Innym rozwiązaniem może być zaprojektowanie API prostu wrócić identyfikatory obiektów zagnieżdżonych:

GET /school/1 

{ 
    id: 1, 
    name: "Jefferson High", 
    students: [1, 2] 
} 


GET /student/1 

{ 
    id: 1, 
    name: "Joel Kim", 
    school: 1, 
    courses: [3, 5] 
} 

Chcemy zawsze „Complete” reprezentacje obiektów o wszystkich swoich referencji, więc jest to dość łatwe do przechowuj te informacje w stronie klienta magazynu danych. Wymagałoby to jednak wielu pobrań, aby pokazać każdy ekran. Aby wyświetlić informacje o uczniu, musielibyśmy sprowadzić ucznia, a następnie pobrać jego szkołę, a także kursy.

Czy istnieje inteligentniejsze podejście, które pozwoliłoby na przechowywanie w pamięci podręcznej tylko jednej kopii każdego obiektu i zapobieganie wyświetlaniu podstawowych ekranów w wielu pobraniach?

+0

Podoba mi się to pytanie i znajduję dyskusję [tutaj] (http://programmers.stackexchange.com/questions/252362/is-it-a-good-idea-to-merge-multiple-http-requests- zaoszczędzić przepustowość) jest pomocna, gdy o tym myślimy. Tytuł twojego pytania wspomina o buforowaniu, ale ciało nie. Jeśli zastosujesz drugie (znormalizowane) podejście, twoja przeglądarka może buforować żądania same, oszczędzając ci kilka podróży na serwer. –

+0

@ this-vidor dzięki za link! Nie brałem nawet pod uwagę buforowania przeglądarki, a jedynie trzymania obiektów ("cache") w magazynie kluczy i wartości w stanie Redux. Myślę, że sposób wykorzystania pamięci podręcznej przeglądarki i stanu Redux byłby szczegółem implementacji, który niekoniecznie zmieniłby odpowiedź na pierwotne pytanie. –

+0

Dla tego, co jest warte, używam Redux i przeglądarki internetowej, ale te same ograniczenia projektowe mogą z łatwością dotyczyć dowolnego innego klienta (na przykład natywnej aplikacji) za pomocą innego magazynu danych po stronie klienta i jakiegoś innego klienta HTTP z pamięcią podręczną. –

Odpowiedz

3

Możecie być mieszanie dwóch pojęć: Storage i reprezentacje.Możesz przywrócić nienormalizowaną reprezentację (pierwszą sugerowaną opcję) bez również przechowywania tych "częściowych" obiektów w bazie danych.

Proponuję więc spróbować zwrócić nienormalizowane reprezentacje, ale przechowując je znormalizowane (jeśli używasz relacyjnej bazy danych).

Sugestia poprawy: możesz użyć odpowiednich identyfikatorów URI zamiast identyfikatorów w swoich reprezentacjach. Prawdopodobnie chcesz, aby klienci wiedzieli "gdzie", aby uzyskać ten obiekt, łatwiej jest więc podać identyfikator URI. W przeciwnym razie klient musi wymyślić, jak utworzyć identyfikator URI z identyfikatora, który zazwyczaj kończy się zakodowaniem w kliencie, co nie jest możliwe w usłudze REST.

+0

Wydaje mi się to dość rozsądnym podejściem do mnie. Tak więc w przykładzie, kiedy chcemy pokazać widok szkoły, pobieramy do szkoły i przechowujemy znormalizowaną szkołę bez przechowywania żadnych uczniów (ponieważ obiekty uczniów są "częściowo" włączone do reprezentacji). Jednak nadal będziemy używać częściowych uczniów do zapełnienia listy uczniów w widoku szkoły. Problemem, który widzę, jest to, że gdybyśmy mieli powtórzyć ten widok szkolny, musielibyśmy zrobić to ponownie, ponieważ szkoła byłaby w magazynie danych, ale uczniowie, do których się odwołali, nie byliby. Wszelkie przemyślenia na temat tego, jak sobie z tym poradzić? –

+0

Przeglądarka będzie/powinna buforować odpowiedź * całą * dla ciebie (jeśli wymagane dyrektywy kontroli pamięci podręcznej są podane przez serwer), więc nie musisz się tym martwić. Możesz powtórzyć żądanie i powinieneś otrzymać buforowany dokument lub serwer może wykonać warunkowe 'GET', które możesz jeszcze zoptymalizować po stronie serwera. Czy budujesz pamięć podręczną od zera po stronie klienta? –

+0

tak, a może naiwnie. Przechowuję każdy z tych obiektów w magazynie klucz-wartość za pomocą Redux (więc pobranie school 1 zapisałoby obiekt w 'state.schools [1]'). Wygląda na to, że buforowanie przeglądarki pomogłoby bardziej tutaj. Gdybym tylko polegał na pamięci podręcznej, nie jestem pewien, czy nadal istnieje możliwość zapisywania obiektów w magazynie klucz-wartość (lub nawet w ogóle korzystania z Redux). –