Artykuł ten jest częścią serii zatytułowanej „Mikroserwisy i Spring Cloud”.

1. Mikroserwisy i Spring Cloud. Wprowadzenie.
2. Mikroserwisy i Spring Cloud. Projekt bazowy.

Projekt bazowy

We wpisie tym opiszę projekt bazowy, który z każdą kolejną odsłoną cyklu będzie coraz bardziej rozbudowywany, i który posłuży nam jako podstawa do testowania rozwiązań dostarczanych przez biblioteki z rodziny Spring Cloud.

Sklep Internetowy

Projektem tym jest sklep internetowy składający się z trzech współpracujących ze sobą mikroserwisów. Są nimi:

  • WarehouseService – serwis udostępniający metody do przeszukiwania magazynu i pobierania z niego produktów,
  • CartService – serwis zarządzający koszykami klientów zalogowanych do systemu,
  • OrderService – serwis odpowiedzialny za proces realizacji zamówienia. W swej pracy komunikuje się z CartService, od którego pobiera informacje o stanie koszyka, i któremu zleca jego czyszczenie (po przetworzeniu zamówienia).

Aby lepiej zrozumieć role poszczególnych mikroserwisów, spójrzmy jak mogłaby wyglądać ich wzajemna komunikacja w procesie przygotowywania i realizacji zamówienia:

  • Po wejściu na stronę sklepu klient otrzymuje od WarehouseService listę dostępnych w sklepie produktów.
  • Klient wybiera z listy interesujący go produkt i wysyła do CartService żądanie dodania tego produktu do swojego koszyka.
  • Klient realizuje zamówienie poprzez wysłanie żądania do OrderService, który kolejno pyta CartService o zawartość koszyka klienta, przetwarza zamówienie, a na koniec ponownie komunikuje się z CartService, tym razem w celu wyczyszczenia koszyka.

Czy taki model ma sens z biznesowego punktu widzenia? Prawdopodobnie nie. Nie jest jednak moim celem implementowanie skomplikowanych procesów, a jedynie stworzenie „piaskownicy” do zabawy springowymi rozwiązaniami. W związku z powyższym tworzony kod będzie w wielu miejscach uproszczony i „dziurawy” (nie pokryje wszystkich przypadków użycia, brak będzie dokładnej obsługi błędów). Początkowo też zrezygnujemy z tworzenia dedykowanego interfejsu graficznego oraz z implementowania security i logiki do zarządzania użytkownikami.

WarehouseService

Zadaniem WarehouseService jest udostępnianie metod służących do zarządzania stanem magazynu sklepowego. Stan magazynu definiowany jest przez zbiór znajdujących się w nim produktów, w związku z czym głównym obiektem domenowym w tym serwisie jest Product.

Tworzymy dla niego repozytorium.

Oraz serwis implementujący logikę biznesową dotycząca pobierania i przeszukiwania listy produktów.

Powyższy serwis nie zawiera żadnych metod służących do dodawania/usuwania przedmiotów z magazynu. Nie implementujemy ich, ponieważ póki co żaden z klientów nie potrzebuje takiej funkcjonalności.

Ostatnim kluczowym elementem jest kontroler.

CartService

Tak jak dla WarehouseService głównym obiektem domenowym jest Product, tak dla CartService jest nim Cart.

Każdy koszyk przypisany jest do konkretnego użytkownika (userId) oraz zawiera listę znajdujących się w nim produktów (products). Produkty te oczywiście odnoszą się do obiektów z WarehouseService, lecz do ich reprezentacji używamy lekko „odchudzonej” wersji klasy Product z tamtego serwisu.

W porównaniu do Product z WarehouseService nie mamy tutaj pola name. Z punktu widzenia koszyka jest ono nieistotne, więc nie zaprzątamy sobie nim głowy.

Dlaczego nie współdzielimy kodu klasy Product pomiędzy tymi dwoma serwisami? Bo tak jest najprościej. 🙂 Gdybyśmy mieli bibliotekę enkapsulującą wszystkie encje używane w systemie i byłaby ona zależnością dla każdego z tworzących system mikroserwisów, to mogłoby to sprawić wiele problemów. Co gdyby jeden z serwisów chciał dołożyć jedno pole więcej do encji i operować tak zaktualizowaną jej postacią? Wtedy każdy inny serwis musiałby się dostosować, a być może tylko jeden z nich byłby faktycznie zainteresowany tą zmianą.

Logika biznesowa zarządzania koszykiem znajduje się w klasie CartService.

Jednym z pól tej klasy jest cart. W celu uproszczenia implementacji przyjęliśmy założenie, że z systemu będzie korzystał tylko jeden użytkownik. W związku z tym CartService póki co będzie miał na stałe „zaszyty” koszyk tego jednego użytkownika.

Pozostał jeszcze kontroler.

OrderService

Ostatnim serwisem w naszym skromnym systemie jest OrderService. Jako że pomijamy szczegóły techniczne procesu realizacji zamówienia, to jest to jeden z prostszych implementacyjnie komponentów. Punktem wejścia do niego jest OrderController.

Tak jak i pozostałe dwa kontrolery, tak i ten jest bardzo „chudy” i jedyne co robi to oddelegowuje pracę do warstwy serwisów.

Implementacja OrderService raczej nie wymaga komentarza. CartService natomiast to warstwa abstrakcji, pod którą kryją się rest-owe wywołania do faktycznego serwisu.

Przykład działania

Kody źródłowe powyższych projektów można znaleźć na moim GitHubie. Aby uruchomić cały system wystarczy w folderze  00 - base project wywołać komendę  ./gradlew bootRun --parallelWarehouseServiceCartServiceOrderService zaczną nasłuchiwać na portach odpowiednio 8030, 8040 i 8050.

Przykładowa sekwencja zapytań może wyglądać następująco.

Podsumowanie

Przedstawiony projekt pozbawiony jest jakichkolwiek zależności względem rozwiązań z rodziny Spring Cloud. Postaramy się dodawać je cegiełka po cegiełce, stopniowo budując pełnoprawny system. Zaczniemy, już w następnym wpisie, od biblioteki Open Feign.