Rozwój technologii pojawia się w wielu aspektach - wykorzystywaniu innych materialów, lepszym połączeniu poszczególnych komponentów, zwiększeniu liczby elementów itd. Jednak jedną z najbardziej widocznych cech odróżniającą obecne urządzenia elektroniczne od tych sprzed kilkunastu lat jest rozmiar. Nie da się ukryć, że postęp techniki wiąże się również z miniaturyzacją w myśl zasady, że im mniej miejsca coś zajmuje, tym więcej tego czegoś się zmieści. Ta zasada dotyczy sprzętu, ale może być również zastosowana w oprogramowaniu, choć w nieco innym ujęciu. Tutaj niby też mamy do czynienia ze zmniejszeniem rozmiaru aplikacji oraz zwiększeniem ich liczby. Ale to jednak nie wszystko, gdyż celem tego procederu nie jest zawsze tylko przyspieszenie aplikacji.
Dzisiaj wyjaśnimy sobie, czym są mikroserwisy i kiedy ujawniają się ich zalety. Przy okazji, ponieważ mikroserwisy wiążą się stricte z backendem, dla równowagi powiemy sobie o nieco rzadziej wspomnianej koncepcji mikrofrontendów, a na deser przywołamy jeszcze zjawisko mikroframeworków. Krótko mówiąc, dzisiejszy tekst sponsoruje słowo "mikro", choć niekoniecznie będzie to widać w jego długości. Brakuje jeszcze mikrokontrolerów do kompletu, ale to nie do końca te rejony informatyki, w których się poruszamy.
W tym artykule bardzo często będą pojawiać się sformułowania "frontend" oraz "backend". Jeśli te terminy są dla Ciebie jeszcze niejasne, polecamy zapoznać się najpierw z naszym innym tekstem.
Co to są mikroserwisy?
Aby wyjaśnić sobie to pojęcie, należy w ogóle przypomnieć, czym są serwisy, a właściwie web serwisy, zwane też po polsku usługami sieciowymi. Są to zasoby w sieci WWW udostępniane poprzez specjalny interfejs webowy. Tym ostatnim najczęściej jest REST, co oznacza udostępnianie zasobów poprzez żądania HTTP. Zazwyczaj wykorzystywanie takich usług wygląda podobnie do otwierania podstron pod danymi adresami URL, tylko że w tym przypadku wynikiem nie jest HTML, ale najczęściej dane w postaci XML lub JSON. Web serwisy służą jako backend aplikacji, czyli tą część, która bezpośrednio "styka" się z bazą danych i odpowiada zarówno za dostarczanie danych, zapisywanie ich, jak i wykonywanie obliczeń.
A zatem udostępnianie usług sieciowych faktycznie ma miejsce poprzez aplikację, która pod określonymi adresami pozwala uzyskać poszczególne części funkcjonalności. Brzmi w porządku i dość prosto, ale im system staje się większy, tym zaczyna pojawiać się coraz więcej problemów. Po pierwsze, duża aplikacja szybko staje się dość skomplikowana ("bałagan" rośnie tym wolniej, im lepszy kod jakościowo powstaje), co może przyspieszyć powstawanie błędów. Po drugie, jedna aplikacja sprawia, że w przypadku bardzo obciążonych serwisów, trudniejsze staje poradzenie sobie z napływającym ruchem. Wówczas bardzo często, w imię skalowania, powiela się backend, stawiając go na kilku serwerach, jednak nie zawsze jest to możliwe, wygodne w konfiguracji, a ponadto duża aplikacja powielona kilkukrotnie siłą rzeczy wymaga lepszej maszyny. Po trzecie, wdrożenie zmian w jednostkowym backendzie jest nie tylko trudniejsze, ale i wymaga zatrzymania działania systemu, aby podmienić wersję na nową (oczywiście, w najbardziej podstawowym przypadku).
To wszystko sprowadza nas do tego, że dla bardzo wymagających i rozbudowanych systemów powinniśmy szukać innego rozwiązania. I takie już jakiś czas temu opracowano oraz stosuje się z powodzeniem - polega na podzieleniu jednej aplikacji backendowej na kilka mniejszych, z których każda odpowiada danemu zakresowi funkcjonalnemu. Tak - może to oznaczać, że zamiast jednego backendu, będziemy mieć ich kilka, kilkanaście lub nawet więcej. Gdzie są ulokowane zalety?
Po pierwsze, jako że mikroserwisy są osobnymi aplikacjami, dużo mniejszymi niż jeden cały backend, to łatwiej je utrzymać oraz skupić się na danym obszarze domenowym systemu (czyli grupie powiązanych ze sobą funkcji). Mogą też być bardziej dynamicznie projektowane, gdyż w razie czego zmianie ulegnie tylko jeden mały kawałek kodu, a nie ogromny system (oczywiście, wszystko zależy od przypadku).
Po drugie, ma to dużo zalet z punktu widzenia wdrażania i utrzymania. Zauważmy, że małą aplikację łatwiej powielić niż dużą. To oznacza, że mikroserwisy bardzo sprzyjają skalowaniu i replikacji. Co jednak najważniejsze, ponieważ poszczególne podaplikacje są rozwijane niezależnie od siebie, ich wdrażanie również jest niezależne. To dlatego, gdy np. bank ogłasza godziny serwisowe, to może określić, które usługi będą nadal działać, a które nie - to rezultat tego, że nie trzeba podmieniać całego backendu na serwerze, ale tylko jego poszczególne części. Oczywiście, to duże uproszczenie, ale ważne jest zrozumienie tej koncepcji.
Po trzecie, mikroserwisy są lepsze z punktu widzenia zarządzania zespołem. W przypadku bardzo dużych backendów liczba programistów również jest duża, co utrudnia kierownikowi projektu zapanowanie nad wszystkim. Jeśli podzielimy backend na mniejsze części, którymi będą zajmowały się poszczególne grupy, z których każda jest niezależna, to nagle monitorowanie postępów i reagowanie na problemy staje się łatwiejsze, gdyż każdy ma swoje "podwórko" i nie wchodzi innym w drogę. Można tutaj też przywołać zasadę "dwóch pizz" wprowadzoną przez Jeffa Bezosa (byłego CEO Amazonu), która mówi, że zespół powinien być taki mały, aby dwie pizze wystarczyły na posilenie wszystkich jego członków.
Po czwarte, ponieważ mikroserwisy sa niezależnymi aplikacjami, to nie muszą być pisane w jednej technologii. Oczywiście, nie należy z tym przesadzać i nie robić każdego serwisu w innym języku lub frameworku, ale zdarzają się sytuacje, w których dana część funkcjonalna jest zdecydowanie łatwiejsza lub sensowniejsza do wykonania np. w Pythonie, a inna - w Javie. W praktyce występują także okoliczności, sprawiające, że fragment systemu musi cechować się wyjątkowo wysoką wydajnością i warto przygotować go w innej technologii, skoncentrowanej stricte na szybkości działania i/lub przepustowości. Warto przeanalizować przypadek twórców gry Gwint, którzy w celu polepszenia architektury serwerowej karcianki połączyli serwisy napisane w PHP oraz Go.
Ponieważ do tej pory wymieniliśmy zalety, to dla równowagi warto również przywołać wady mikroserwisów, gdyż te również istnieją. A są związane głównie z poziomem skomplikowania - co prawda, każda aplikacja mikroserwisowa jest mniejsza i wymaga mniejszego zespołu, natomiast sama architektura całego systemu oparta o małe usługi jest dość złożona i wymaga choćby wstępnego zaplanowania, a także dobrego zaplecza administracyjnego i DevOps-owego. Samo ustawienie całej infrastruktury w taki sposób, aby wygodnie można było korzystać z mniejszych aplikacji jest skomplikowaną sprawą i czasami wymaga osobnego zespołu zajmującego się tylko tym aspektem. Krótko mówiąc - nie ma sensu wprowadzać mikroserwisów tam, gdzie zwykła aplikacja backendowa jest całkowicie wystarczająca i zaspokaja potrzeby wydajnościowe, przepustowe oraz funkcjonalne. Pamiętajcie, że w razie potrzeby i rozrostu systemu, możliwe jest przeprojektowanie backendu i "wyciągnięcie" z niego kawałków, które będą składały się na poszczególne mikroserwisy (choć - oczywiście - łatwiej to robić od razu).
Warto również podkreślić to, że o ile wdrażanie pojedynczych aplikacji jest proste, to utrzymanie całej infrastruktury może nastręczać trudności mniejszym (i nie tylko) zespołom. Dlatego mikroserwisom w sukurs przyszła konteneryzacja i spopularyzowane zostały takie narzędzia jak Docker, Podman, containerd czy Kubernetes. Dzięki tej koncepcji dużo prostsze stało się zarządzanie poszczególnymi elementami systemu oraz przede wszystkim ich wdrażanie. Dodatkowo, kontenery rozwiązują problem konfliktów różnych wersji bibliotek potrzebnych dla poszczególnych usług i to zarówno na serwerze, jak i komputerze programisty. Przykładowo, konteneryzacja pozwala łatwiej utrzymać jednocześnie jedną aplikację napisaną w PHP 7.4 oraz drugą, korzystająca z dobrodziejstw PHP 8.1. Oczywiście, mikroserwisy nie wymagają kontenerów, a kontenery nie wymagają mikroserwisów, natomiast nie da się ukryć, że podzielenie oprogramowania na mniejsze aplikacje bardzo współgra z koncepcją proponowaną np. przez Dockera.
Co to są mikrofrontendy?
Ktoś słusznie może zapytać, że skoro backend można podzielić na mniejsze aplikacje, to czy to samo dotyczy frontendów. Odpowiedź brzmi: tak, ale inaczej i jest to chyba mniej spopularyzowane.
Tutaj warto zauważyć, że wcale nierzadkim przypadkiem jest dzielenie frontendu na osobne panele, skierowane do poszczególnych grup użytkowników. Przykładowo, portal edukacyjny może mieć osobną aplikację dla uczniów, rodziców oraz nauczycieli - każda z nich będzie wyglądała podobnie, ale np. wyróżniała się kolorem i oferowała inną funkcjonalność. W takim układzie najczęściej poszczególne panele znajdują się pod osobnymi subdomenami. Nie jest to jednak coś, co nazwalibyśmy mikrofrontendami - co prawda, to całe osobne aplikacje, które o sobie "nie wiedzą" i nie są ze sobą połączone. Może to być wręcz jedna witryna, która wewnętrznie wyświetla się nieco inaczej i udostępnia różne rzeczy w zależności od roli użytkownika, który się zaloguje.
W przypadku mikrofrontendów chodzi o coś innego - tutaj jeden interfejs fizycznie jest dzielony na części, którymi zajmują się osobne zespoły. O ile w przypadku mikroserwisów mogliśmy taki podział wyobrazić sobie jako zwiększenie liczby "wysepek", które oferują różne funkcje i klient (jako aplikacja kliencka) kieruje się do konkretnej z nich, o tyle w przypadku interfejsu podzielonego na części mówimy bardziej o budowaniu jednego panelu z klocków, które de facto są innymi aplikacjami. W ten sposób istnieje jedno oprogramowanie typu "master", które stanowi bazę wyświetlaną użytkownikowi, natomiast może on wewnątrz siebie pokazywać elementy, z których każdy w tle jest tak naprawdę osobnym oprogramowaniem i - podobnie jak przy backendzie - w ostateczności może być napisane w innym języku. Starsi czytelnicy pasjonujący się tworzeniem stron mogą pamiętać koncepcję ramek (ang. frames) - jeśli ramki byłyby osobnymi aplikacjami, to nazywalibyśmy je właśnie mikrofrontendami.
Ma to te same zalety, co w przypadku backendu - dla bardzo złożonych aplikacji usprawnione zostaje zarządzanie rozwojem oprogramowania oraz wdrożenia. Natomiast wprowadza też potrzebę składania tego w całość, co na wstępie jest problematyczne, a wręcz niepotrzebnym bagażem dla niewielkich firm i/lub niewielkich aplikacji. Dlatego mikrofrontendy nie występują często, co nie oznacza, że koncepcja jest zła - po prostu każdą ideę trzeba dostosowywać i korzystać z niej w środowisku, które rzeczywiście ją potrzebuje.
A jak to "sklejanie" odbywa się technicznie? Na różne sposoby - istnieją określone frameworki, ale można tutaj zastosować również starego dobrego iframe'a
. Więcej szczegółów znajdziecie pod poniższymi linkami:
- Micro Frontends - extending the microservice idea to frontend development
- Czym jest i jak wykorzystać mikrofrontend
- 5 Different Techniques for Cross Micro Frontend Communication
Co to są mikroframeworki?
Na deser pozostawiliśmy sobie jeszcze inne pojęcie, które z pozoru nawiązuje do wcześniejszych "mikroterminów", ale w praktyce jest czymś zupełnie innym. Wspominaliśmy o tym, że do budowania aplikacji webowych (i nie tylko) wykorzystuje się różne frameworki. Niektóre z nich są bardziej uznane, inne mniej, ale w tej chwili bardziej interesuje nas fakt, że w większości są one "silnikami", które zawierają narzędzia potrzebne do realizacji prawie każdego aspektu oprogramowania, za wyjątkiem bardzo specyficznych rozwiązań. Mam tutaj na myśli uznane rozwiązania typu Yii 2.0, Laravel, Symfony, Spring, NestJS, Django, .NET Core czy inne.
Ale czasami programiści nie muszą stawiać dużych aplikacji, które przetrwają lata i przygotowywać ich struktur przez dłuższy czas. Co więcej, nie zawsze jest czas, aby uczyć się zasad panujących w dużym frameworku tylko po to, aby utworzyć małe API lub gdy wiemy, że technologia w przyszłości i tak się zmieni. Dlatego w eterze pojawiły się mikroframeworki, które są naprawdę niewielkimi silnikami, obsługującymi tylko kluczowe elementy np. aplikacji webowych. I to te naprawdę kluczowe - nie ma tutaj np. zarządzania sesją, połączeniem z bazą danych, walidacji itd. Jeśli programista potrzebuje te komponenty, musi je napisać sam lub dobrać odpowiednie biblioteki. Ale właśnie dzięki temu mikroframeworki nadają się do prostych zadań, do których można zaliczyć m.in. szybkie API na potrzeby większej aplikacji lub jakiegoś testu. Z racji swojego rozmiaru, nauka danego rozwiązania jest dość prosta i nie zajmuje dużo czasu.
Wbrew pozorom, ani mikroserwisy ani mikrofrontendy nie są zazwyczaj tworzone przy użyciu mikroframeworków. Pamiętajmy, że mimo małego rozmiaru, osobne aplikacje po stronie frontendu lub backendu są pełnoprawnymi kawałkami oprogramowania, w których obsługiwane są różne przypadki i np. uwierzytelnianie oraz autoryzacja użytkownika. Co za tym idzie, każda z nich wymaga zastosowania może nie "ciężkiej artylerii", ale całościowego narzędzia - dlatego najczęściej wybiera się do nich "normalne" frameworki. Nie oznacza to, że te w skali mikro są bezużyteczne, ale raczej mają po prostu inne zastosowanie.
Przykładowe i bardziej znane mikroframeworki to Slim, Lumen (PHP), Bottle, Flask (Python), Spark (Java), Sinatra (Ruby) czy Express.js (Node.js). Całkiem niedawno pojawił się również Framework X, który ma zastosowanie typowo w tworzeniu API i wygląda na ciekawe rozwiązanie, skupiające się na czymś, z czego PHP nie jest znane - asynchroniczności.
Podsumowanie
Ten artykuł miał na celu wyjaśnić ideę mikroserwisów oraz mikrofrontendów. Oba te terminy są związane z dzieleniem aplikacji na mniejsze części, które łatwiej się wdraża oraz pozwalają lepiej rozdzielić odpowiedzialność w firmie IT. Tym niemniej, należy mieć na uwadze fakt, że podobnie jak każde narzędzie w procesie powstawania oprogramowania, także te omówione mają swoje konsekwencje i wiążą się z szerzej zakrojoną konfiguracją, aby spiąć wszystkie części - dlatego są to rozwiązania przeznaczone raczej dla dużych systemów lub takich, których konkretne fragmenty muszą zostać specjalnie potraktowane. Na deser przywołaliśmy istnienie mikroframeworków, mimo że nie są one ideowo związane z poprzednimi zagadnieniami.
Pozdrawiam i dziękuję - Jakub Rojek.