Zwinna droga do mikroserwisów

Michał Drożdż

Zaktualizowaliśmy ten tekst dla Ciebie!
Data aktualizacji: 27.12.2024
Autor aktualizacji: Łukasz Piotrowski

Co jeśli wiesz, że będziesz potrzebować architektury mikroserwisowej, ale nie wiesz, jak podzielić wymagania biznesowe na małe, niezależne usługi?

Na początku projektowania systemu istnieje wiele założeń i przewidywań dotyczących ruchu, obciążenia i potrzeb wydajnościowych. Czasem możesz mieć więcej pytań niż odpowiedzi. Co jeśli wiesz, że będziesz potrzebować architektury mikroserwisów, ale nie wiesz, jak podzielić swoje wymagania biznesowe na małe, niezależne usługi? W rzeczywistości rozpoczęcie budowy architektury mikroserwisowej nie oznacza, że od razu zaczynasz budować mikroserwisy. Jest opcja, która może Ci się spodobać.

Istnieje podejście, które prowadzi do architektury mikroserwisów, ale zaczyna od prostego monolitycznego rozwiązania. Najważniejsze jest, aby stworzyć projekt monolitu, który umożliwi Ci łatwe wyodrębnianie poszczególnych fragmentów logiki biznesowej do mikroserwisów. Co więcej, Java została zaprojektowana tak, aby wspierać to podejście.

Brzmi dobrze… ale jak to zrobić?

Chodzi o modyfikatory dostępu i enkapsulację. Te dwa pojęcia współgrają ze sobą. W Javie istnieją cztery (nie trzy!) modyfikatory dostępu: *default*, *public*, *protected* i *private*. Język ten oferuje również trzy poziomy enkapsulacji: klasa, pakiet i moduł. Ta wiedza wystarczy, aby zaimplementować opisane poniżej założenia.

Po pierwsze, musimy podzielić logikę biznesową na pakiety. Ważne jest, aby dokonać tego podziału według odpowiedzialności biznesowych i umieścić całą powiązaną logikę (klasy) w jednym pakiecie. Pakiety nie powinny być duże, jeśli podzielimy je w przemyślany sposób. To tutaj enkapsulacja odgrywa swoją rolę. Enkapsulujemy każdą część logiki biznesowej w spójny pakiet wraz z całą potrzebną i powiązaną informacją. Jeśli chcesz zmienić coś w obsłudze wysyłki, wchodzisz do pakietu *shipping*. Gdy potrzebujesz zmienić logikę zamówień, przechodzisz do pakietu *order*. Ta idea jest zilustrowana na poniższym obrazku.

Obraz zawierający zrzut ekranu, Prostokąt, tekst, designOpis wygenerowany automatycznie

Drugi aspekt to zachowanie jak największej niezależności pakietów biznesowych, co oznacza ograniczenie dostępu do klas i metod w jak największym stopniu. Można to osiągnąć, korzystając z domyślnego dostępu (ang. *package-private*). Prawie wszystkie klasy w pakiecie powinny być *package-private*, podobnie jak metody, które udostępniają. Oczywiście potrzebujemy publicznego interfejsu (fasady), wyjątków i klas do przesyłania danych, ale to wszystko, co powinno być publiczne. Ogólny schemat dostępu przedstawiono poniżej.

Zasady projektowania oparte na enkapsulacji i modyfikatorach dostępu:

  1. Zachowanie spójności w obrębie pakietu:
    1. Wszystkie klasy w pakiecie powinny być ze sobą ściśle powiązane.
    2. Logika wewnętrzna nie powinna być dostępna z zewnątrz.
  2. Kiedy używać publicznych klas interfejsów:
    1. Publiczne klasy i metody powinne być tworzone jedynie wtedy kiedy są wymagane przez zewnętrzne moduły.
    2. Interfejsy powinne być proste i jasno określać kontrakt biznesowy.
  3. Izolacja implementacji:
    1. Klasy wspierające winne mieć modyfikator dostępu default.
Obraz zawierający tekst, zrzut ekranu, diagram, CzcionkaOpis wygenerowany automatycznie

Ta bardzo podstawowa koncepcja projektowania aplikacji w Javie, często pomijana, pozwala programistom na wyodrębnienie każdego pakietu i stworzenie nowego, niezależnego mikroserwisu przy minimalnym wysiłku. Wszystko, co jest potrzebne dla nowej usługi, znajduje się w pakiecie. Po stronie klienta wystarczy zaimplementować interfejs udostępniany przez nową usługę zdalną i wywoływać ją przez sieć zamiast metod wewnętrznych w tej samej aplikacji.

Spring Framework wspiera również tego rodzaju projektowanie aplikacji. Spring działa poprawnie z klasami, które mają domyślny dostęp, i rejestruje je w kontekście jako beany. Co więcej, nie wszystkie klasy muszą być beanami Springa. Tylko te, które są używane w innych pakietach (fasady/kontrolery), a reszta potrzebnych klas może być instancjonowana w każdej klasie konfiguracji pakietu. Daje to programistom większą kontrolę nad zachowaniem aplikacji i jej konfiguracją, a także zmniejsza liczbę beanów w kontekście.

To podejście pozwala na naturalne wdrożenie zasady single reposibility principle (SRP) w każdym pakiecie, każda klasa odpowieda za jedną określoną funkcjonapność ci pozwala na prostsze utrzymania kodu i większą czytelność i identyfikacje potencjalnych błędów.

Aco za tym idzie, zmniejszymy ilość branów w kontekście springa i zwiększymy wydajność aplikacji ale również skrócimy czas uruchomienia aplikacji i uproszcim debugowania. W przypadku dużych aplikacji, to podejście pozwala na zachowanie porządku i unikanie zbędnego obciążenia kontenera IoC.

Dodatkowo można pokusić się o wprowadzenie zasady YAGNI (You Aren’t Gonna Need It), która powinna się opierać na aktualnych potrzebach a nie wymyślonych scenariuszach. W skrócie oznacza to że tworzymy kod który wykonuje aktualne potrzeby biznesowe, a nie rozwijamy kodu który w przyszłości może być wykorzystywany. 

Również same testu jednostkowe, winne być ułożone w spójny logicznie pakiet i być odpowiednio testować kod. Czego skutkiem będzie spójność i bezpieczeństwo wyodrębnionego kodu.

Można też wprowadzić do procesu ciągłą refaktoryzacje zgodną z zasadami agile. Aktualne przegląda kodu i refaktoryzacja pakietów pozwolą na szybszy rozwój mikro serwisu.

DDD jako metoda podziału granicy kontekstów, identyfikacja pakietów, niemieszanie ich i zastosowanie odzwierciedlenia domeny biznesowej pozwoli na jeszcze sprawniejsze przekształcenie aplikacji monolitycznej w serwis.

Opisane podejście wzmacnia elastyczność i skalowalność architektury systemu. Każda część aplikacji może być łatwo wyodrębniona i skalowana w górę, gdy wzrasta jej wykorzystanie.

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami