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?
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. –
@ 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. –
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ą. –