Agregacja danych z różnych mikroserwisów.

0

Cześć,
napotkałem kilka problemów w zadaniu które mam do zrobienia, a wygląda to tak:
Mają być 3 aplikacje springbootowe w oddzielnych kontenerach: Orders, Clients, Products
Orders ma wystawiać restową usługę getOrders (tu domyślam się, że chodzi po prostu o endpoint orders) i createOrders (tak samo endpoint orders tylko obsługa zapytania POST)
Chcę wysyłać POST z jsonem zawierającym dane klienta, produktu i zamówienia.

No i teraz Orders ma się skomunikować z Clients i Products, przekazać im dane które wyciągnie z jsona oraz kod zamówienia które będzie stworzone i z którym mają być powiązani klienci i produkty, a każda z aplikacji ma zapisać dane do swojej bazy.

Głównie męczą mnie dwa problemy.
Po wywołaniu getOrders mam dostać wszystkie zamówienia ze szczegółami. No i tu już mam zagwozdkę, bo sięgam sobie do bazy po id wszystkich zamówień i muszę zapytać pozostałe aplikacje o klientów i produkty które dotyczą tych zamówień. Jakby to było jedno zamówienie do wykorzystałbym GET i przekazał id jako parametr, ale skoro mają to być id wszystkich zamówień to pomyślałem, żeby wysłać jsona. W GET ponoć nie powinno się dodawać body, natomiast POST ponoć powinno służyć do dodawania a nie pobierania elementów, więc trochę się skołowałem co tu zrobić.
Drugą sprawą są modele obiektów. W aplikacjach Client i Product zrobię sobie klasę Client i Product i zmapuję otrzymanego jsona na obiekty i wyślę do bazy.
Natomiast w aplikacji Orders, kiedy otrzymam jsony od pozostałych aplikacji to co powinienem zrobić? Tutaj też stworzyć klasy Client i Product?(coś mi tu śmierdzi) Czy może olać mapowanie na obiekty(bo w sumie chyba mi to niepotrzebne), zagregować to jakoś w jednego jsona i odesłać jako odpowiedź? A jeśli zagregować to co się stosuje do takiej agregacji?

0

Temat rzeka :-) Uroki mikroserwisów.
Możesz trzymać szczegóły zamówienia w Orders, i w raz z nimi 'snapshot' interesujących CIę danych z Products i Clients. Dzięki temu jeśli np Klient zmieni nazwę albo adres, to w Orders będzie jego stan - np ten adres, z momentu składania zamówienia. I jest to w sumie najprostsze rozwiązanie w tym momencie dla Ciebie. Zwłaszcza, że z praktycznego punktu widzenia, też dane ze stanu złożenia zamówienia muszą gdzieś być przechowywane.

Co jeszcze można zrobić?
Możesz trzymać kopie danych z Client i Products w Orders. Razem z mechanizmem ich aktualizacji. (co dalej nie rozwiązuje problemu ze zmianą adresu i danymi historycznymi).

Możesz mieć CQRS i osobną bazę ze wszystkimi zamówieniami połączonymi z klientami itd, na potrzeby Twoich widoków, czy tutaj endpointów. Tu też można to rozszerzyć o Event sourcing i projekcje eventów. (I dalej ta sama kwestia dla danych z momentu zamówienia)

Możesz też stworzyć endpointy w P i C które Ci zwrócą dane za każdym razem jak poprosisz. (słabo... bardzo słabo, jak serwis C albo P wyłączony będzie to cały system pada)

Swoją drogą, czy na pewno chcesz tworzyć to w oparciu o komunikację synchroniczną? Jeżeli nie to tym bardziej się przybliżasz do CQRS i osobnej bazy do odczytu i/lub projekcji.

1

Zetknąłem się z 2 podejściami:

  1. Robisz fasadę, która synchronicznie dociąga dane z różnych usług. Proste, ale obniżone SLA, trzeba kombinować co w sytuacji gdy ktoś nie odpowie itd.
  2. Robisz usługę-cache, którą zapylasz eventami. Typowy read model. Plus jest taki, że pod spodem możesz mieć Elastica i zrobić fancy wyszukiwarkę, ale płacisz cenę eventual consistency i opedzania problemów z brakiem danych.
0

Sam temat komunikacji między komponentami jest dowolny i w zasadzie to nie zdecydowałem się jeszcze, także dzięki za info o możliwościach.
Teraz nie do końca mi o to chodzi (a może o to tylko nie wiem :D)

Powiedzmy jest taki case:

  1. Leci zapytanie o zamówienia do mikroserwisu Orders.
  2. Ten pobiera z bazy Order który zamiera tylko kod zamówienia i powiedzmy datę.
  3. Teraz odpytuje pozostałe mikroserwisy o Produkty i Klientów którzy są powiązani z tym zamówieniem.
  4. Dostaje od nich jsony reprezentujące Product i Client (i co z nimi zrobic? zapisać jako string? Nie mam tutaj Klas Product i Client więc nie mam jak zmapować jsonów na obiekty)
  5. Chcę stworzyć jsona opisującego Order i go zwrócić.(Gdybym miał klasy Product i Client to mógłbym sobie je dodać do klasy Order i jakimś jacksonem stworzyć i zwrócić jsona)
    I problem jest taki, że nie wiem jak rozwiązać punkty 4 i 5
1

O panie, to dyskusja na innym poziomie zupełnie xD nie możesz zwrócić swojego DTO, który zawiera potrzebne dane?

1

No brakuje mi podstaw cóż... :D
Czyli jeśli dobrze zrozumiałem ideę obiektów DTO:

  1. W aplikacji Orders tworzę sobie klasę ClientDTO.
  2. W aplikacji Clients tworzę sobie klasę Client oraz ClientDTO.
  3. Z Orders ślę pytanie do Clients o klienta
  4. W Clients odbieram pytanie, pobieram z bazy klienta do obiektu client.
  5. W Clients mapuję sobie client na clientDTO (bo może nie potrzebuję wszystkich pól) i odsyłam jsona zrobionego z clientDTO
  6. W Orders odbieram jsona i mapuję go sobie na obiekt clientDTO.
  7. W Orders tworzę sobie OrderDTO który składam z clientDTO
  8. Odsyłam json zrobiony z OrderDTO
1

Coś w ten deseń

0
TyDraniu314 napisał(a):

Głównie męczą mnie dwa problemy.
Po wywołaniu getOrders mam dostać wszystkie zamówienia ze szczegółami. No i tu już mam zagwozdkę, bo sięgam sobie do bazy po id wszystkich zamówień i muszę zapytać pozostałe aplikacje o klientów i produkty które dotyczą tych zamówień. Jakby to było jedno zamówienie do wykorzystałbym GET i przekazał id jako parametr, ale skoro mają to być id wszystkich zamówień to pomyślałem, żeby wysłać jsona. W GET ponoć nie powinno się dodawać body, natomiast POST ponoć powinno służyć do dodawania a nie pobierania elementów, więc trochę się skołowałem co tu zrobić.

Ktos wywołuje getOrders (weź wszystkie zamówienia, wg. parametrów podanych w query parameters)
Sięgasz do własnej bazy i tworzysz lokalnie listę zamówień, bez szczegółów dotyczących order i client bo ich nie masz
Przygotowujesz sobie listy wszystkich unikalnych client id i product id
Wysyłasz prośby o dane do serwisów client i products o uzupełnienie danych odpowiednio do odpowiedzialności
Składasz wszystko do kupy, konwertujesz do finalnego dto, odsyłasz do klienta

w GET nie powinno się wysyłać body (nie można), parametry powinny być w query URLa. Ale jak masz mieć tych parametrów 100, to zaczyna się robić problem, bo protokół http ogranicza długość url. W takich wypadkach warto przestać się przejmować konwencją REST i zrobić, żeby działało i było czytelne. Np. robisz sobie endpoint:
POST /users/query
i przesyłasz tam jakiś obiekt z listą interesujących cię idków

Drugą sprawą są modele obiektów. W aplikacjach Client i Product zrobię sobie klasę Client i Product i zmapuję otrzymanego jsona na obiekty i wyślę do bazy.
Natomiast w aplikacji Orders, kiedy otrzymam jsony od pozostałych aplikacji to co powinienem zrobić? Tutaj też stworzyć klasy Client i Product?(coś mi tu śmierdzi) Czy może olać mapowanie na obiekty(bo w sumie chyba mi to niepotrzebne), zagregować to jakoś w jednego jsona i odesłać jako odpowiedź? A jeśli zagregować to co się stosuje do takiej agregacji?

Tak, w usłudze orders powinieneś mieć zdefiniowane klasy na product i client.
Powodem jest wymóg loosely coupled services, czyli każda z usług w pełni kontroluje to co robi.
Musisz też mieć zdefiniowane api na wyjściu, bo to usługa odpowiada za to co produkuje. Inaczej będziesz mieć sytuację, gdzie jeden ze schowanych głęboko serwisów zacznie np. rzucać XML zamiast JSON, a cała reszta zamiast spuścić takie odpowiedzi na drzewo zacznie z nich coś sklejać. Przykład ekstremalny, ale jak zamiast ładnie formatowanego JSONa dostaniesz brzydko formatowany, to wynalazki działające na stringach się wysypią.
Ujmując sprawę inaczej - kontrakt pomiędzy serwisami jest zapisany w jakimś tam OpenAPI. Masz tam ścieżkę do endpointu, metodę i zwracaną strukturę. Nie możesz zakładać niczego więcej.

0

Dzięki wielkie za pomoc :) Dużo mi to pomogło.

0

Nie pracowałem z mikroserwisami, więc moje sugestie to czyste dywagacje, ale pytanie czy w ogóle potrzebujesz te dane agregować po stronie backendu? Nie lepiej w interfejsie użytkownika odpytać te 3 serwisy o potrzebne dane dotyczące zamówienia? Trudno mi uchwycić sens agregowania tylko na potrzeby wyrzucenia tych danych razem, w innym miejscu niż na froncie. Szczególnie jeśli sobie pomyślę, że jednym z celów przejścia na rozproszony system jest chęć obsługi dużego ruchu, a agregowanie tych danych w ten sposób utrudnia chociażby cachowanie. Tak jak to sobie wyobrażam to Produkty, Klienci i Zamówienia mają inny cykl życia co sprawia, że ciężko będzie sensownie cachowac dane z getOrders jeśli zwracane dane w tym endpoincie to będzie coś więcej niż dane zamówienia, id klienta i pozycję zamówienia z tym id produktów.

1 użytkowników online, w tym zalogowanych: 0, gości: 1