UI w epoce mikroserwisów – Micro Frontends i Web Components
Temat mikroserwisów w świecie IT jest ciągle żywy. Jak każda nowość – generuje dużo szumu i wzbudza wiele kontrowersji.
Jeszcze do niedawna rozmowy o architekturze mikroserwisowej dotyczyły tylko i wyłącznie backendu aplikacji. Przestarzały, nielubiany i krytykowany przez wielu monolit spychany był w stronę warstwy prezentacji. W pewnym momencie zaczęto się zastanawiać – czy coś nam zaraz nie wybuchnie?
Jak powinno się projektować UI w epoce mikroserwisów?
Czy rozszerzenie idei mikroserwisów na obszar frontendu to dobry pomysł?
Czy aktualnie używane technologie są gotowe na implementację tego podejścia?
Pytania te zainspirowały mnie do własnych poszukiwań odpowiedzi. Ich efektami oraz moimi przemyśleniami wnioskami dzielę się z Wami w tym artykule.
Projektowanie warstwy prezentacji nowoczesnych aplikacji internetowych nie jest proste. Od lat widać tendencję, w której cienki klient (thin client) coraz śmielej zastępowany jest swoim grubszym kolegą (fat client). W konsekwencji na barkach części klienckiej spoczywa coraz więcej obowiązków. W świecie mikroserwisów, CAP Theorem oraz eventual consistency – podejmowanie ostatecznych decyzji coraz częściej leży po stronie logiki aplikacji klienckiej.
Nie bez powodu, my, programiści, śmiejemy się często z tego, że dzień bez nowego, zmieniającego świat frameworka JavaScript, jest dniem straconym.
Właściwie, to dlaczego powstaje tyle nowych frameworków? Czy zastanawialiście się nad tym?
Może dlatego, że praktycznie z miesiąca na miesiąc rosną oczekiwania względem warstwy prezentacji. Ma być coraz szybciej, coraz płynniej, coraz ładniej, coraz dynamiczniej, coraz… Tylko pytanie jest takie, czy korzystając z obecnego podejścia do projektowania sprostanie tym wymaganiom jest jeszcze możliwe?
Może więc zamiast tworzyć coraz to nowe frameworki, powinniśmy skupić się na lepszym wykorzystaniu tych obecnie istniejących?
Czy rozszerzenie podejścia mikroserwisowego na warstwę prezentacji pomoże sprostać tym wymaganiom?
Micro Frontends
Jeśli szukamy odpowiedzi na powyższe pytania powinniśmy zainteresować się podejściem nazywanym Micro Frontends – technikami i strategiami wykorzystywanymi do tworzenia nowoczesnych aplikacji internetowych, które rozwijane są przez wiele zespołów mogących używać różnych technologii.
Rozszerzając tę definicję, w podejściu Micro Frontends możemy wyróżnić następujące punkty:
- Aplikacja, jako zbiór funkcjonalności, z których każda rozwijana jest przez niezależny zespół;
- Każdy zespół posiada oddzielny obszar biznesowy, za który jest odpowiedzialny i w którym się specjalizuje;
- Zespoły są wielofunkcyjne (tzw. full-stack) i rozwijają swoje funkcjonalności od początku do końca, od bazy danych do interfejsu użytkownika;
- Każdy zespół powinien móc zmienić używaną technologię.
Powyższy rysunek w dużym uproszczeniu pokazuje o co chodzi.
Po lewej stronie mamy monolityczny frontend – dużą aplikację napisaną z wykorzystaniem jednego frameworka (Angular/React/Vue).
Po prawej stronie mamy mikro frontend’y – małe komponenty, które mogą być napisane w różnych frameworkach, łączonych następnie w spójną całość.
Warto zaznaczyć, że zastosowanie pierwszego podejścia nie wyklucza modularności wewnątrz warstwy prezentacji.. Zespoły mogą być podzielone wertykalnie (odpowiedzialność za konkretne obszary biznesowe), ale nie mają tyle swobody w tworzeniu swoich kawałków.
Wprowadzenie nowego podejścia i większej swobody może rozwiązywać pewne problemy i w praktyce to robi. Z drugiej strony wymaga podjęcia pewnych decyzji i sprostania wielu wyzwaniom:
- kiedy zintegrować poszczególne części w jedną całość
- podczas budowania
- w runtime po stronie klienta
- w runtime po stronie serwera
- w runtime w jakiejś warstwie pośredniej
- jak podejść do routingu i komunikacji między komponentami/ serwisami
- jak zapewnić prawdziwą niezależność komponentów
- jak uniknąć konfliktów w stylach CSS
- jak ładować komponenty tylko jeśli są potrzebne (lazy loading)
- jak udostępniać zasoby współdzielone (common resources)
W tym momencie nie będziemy skupiać się na powyższych problemach, ponieważ sposób ich rozwiązania zależy od wybranego sposobu implementacji.
Podejście Micro Frontends podczas budowania swoich rozwiązań stosują najwięksi gracze na rynku, w tym między innymi:
Myślę, że branża e-commerce nieprzypadkowo przoduje w stosowaniu tego typu rozwiązań.
Główne funkcjonalności w każdym z portali są znane i dobrze zdefiniowane: wyszukiwarka, koszyk, wybór kategorii, rekomendacje, promowane ogłoszenia, reklamy. Dzięki temu nie zmieniają się dynamicznie, przez co dużo łatwiej można wdrożyć podejście Micro Frontends. Za każdą z wymienionych funkcjonalności może odpowiadać oddzielny komponent napisany z wykorzystaniem innego frameworka.
Poniższy obrazek wyjaśnia to na przykładzie Amazona.
Choć może się to kojarzyć z podejściem Backends For Frontends, to nie są to tożsame pojęcia. Dla Micro Frontends kluczowa jest kompozycja interfejsu z komponentów odpowiadających różnym obszarom biznesowym, które wspierane są przez różne usługi backendowe. W BFF natomiast chodzi przede wszystkim o to, że tworzymy różne interfejs/API Gateway’e dla różnych typów klientów.
Oba te podejścia można oczywiście ze sobą połączyć. Możemy mieć BFF, czyli inne API dla interfejsu aplikacji mobilnej, a inne dla interfejsu aplikacji webowej, a każdy z nich będzie poskładany z komponentów.
W powyższym przykładzie mamy do czynienia z klientem webowym, którego frontend złożony jest z kilku komponentów, a każdy z nich przypisany ma swój własny UI Composition Microservice.
Jak w praktyce można zaimplementować podejście mikroserwisowe w warstwie prezentacji?
- Web Components – umożliwiają tworzenie niestandardowych, reużywalnych komponentów zamkniętych w tagach HTML. Teoretycznie działają we wszystkich nowoczesnych przeglądarkach i mogą być używane z dowolnym frameworkiem.
- Project Mosaic – stworzony przez Zalando zbiór bibliotek do budowy dużych aplikacji internetowych wykorzystujących koncepcję fragmentów.
- single-spa – ciekawe rozwiązanie z kilkoma nieźle działającymi przykładami, ale niestety to następna warstwa abstrakcji w naszym rozwiązaniu (framework do łączenia frameworków).
- Allegro OpBox – rozwiązanie, które nie jest udostępnione jako open source i nie będzie dopóki jakiś duży gracz zadeklaruje chęci i planów jego kontrybuowania. Warto jednak przeczytać podlinkowany artykuł, ponieważ zastosowane podejście jest naprawdę ciekawe.
Rozpoznając ten temat i analizując powyższe możliwości, postanowiłem skupić się na Web Components.
Web Components
Web Components składają się z następujących elementów (nazywanych również specyfikacjami):
- Custom Elements, które definiują jak budować i używać nowych elementów drzewa DOM,
- Shadow DOM, dzięki któremu można ukrywać szczegóły implementacji, czyli stosować enkapsulacje,
- HTML imports, które pozwalają wykorzystywać zdefiniowane wcześniej dokumenty HTML w innych dokumentach,
- HTML Template, dzięki któremu możemy zdefiniować fragmenty, które inicjalizowane są w momencie, kiedy stają się potrzebne.
Te krótkie definicje mogą nie do końca tłumaczyć, o co w tym wszystkim chodzi, dlatego zobrazuję je na przykładach.
Co możemy osiągnąć stosując Custom Elements, czyli niestandardowe znaczniki HTML, które mogą być naszymi komponentami?
Nasz kod HTML aktualnie bardzo często wygląda mniej więcej tak:
Dzięki zastosowaniu Custom Elements może on wyglądać tak:
Prawda, że jest lepiej?
Shadow DOM, jak już wcześniej napisałem pozwala stosować enkapsulacje. Wiedzieliście, że nawet zwykły input ma w sobie ukryte dwa div’y?
Normalnie tego nie widzimy, ponieważ standardowo opcja pokazywania Shadow DOM dla standardowych elementów HTML jest wyłączona, ale bardzo łatwo można ją włączyć.
HTML Import wiąże się z użyciem znacznika link w konkretnym elemencie, do którego chcemy wstrzyknąć kawałek HTMLa znajdującego się w innym pliku.
HTML Template tworzy się przez użycie znacznika template. Następnie za pomocą JavaScriptu, w wybranym przez nas momencie możemy wstrzyknąć ten kawałek kodu do innego elementu.
Według strony webcomponents.org wszystkie cztery główne składowe Web Components już działają na najpopularniejszych przeglądarkach:
Web Components – współpraca z frameworkami
Tylko kto w dzisiejszych czasach pisze aplikacje w czystym JavaScripcie?
Nieliczni 🙂
Kolejnym krokiem jest zatem sprawdzenie jak wygląda wsparcie najpopularniejszych frameworków dla tych rozwiązań. Szukając odpowiedzi na to pytanie, trafiłem na stronę Custom Elements Everywhere, która testuje frameworki pod kątem wsparcia dla Custom Elements. Z najpopularniejszych frameworków testy na 100% przeszedł Angular, AngularJS oraz Vue. Preact jest blisko (91%), React trochę dalej (71%).
Po analizie powyższych wyników, postanowiłem ograniczyć swój eksperyment do Angulara 6 oraz Vue 2.
W wersji 6 Angulara wprowadzono Angular Elements. Jest to swego rodzaju proxy między komponentami Angulara, a API Custom Elements pozwalające opakować komponent Angulara w Custom Element. Jednak angular-cli jeszcze tego nie wspiera i trzeba posiłkować się niestandardowymi skryptami do budowania. Jeśli ktoś z Was chciałby zagłębić się trochę bardziej w ten temat, polecam świetny post na blogu Telerika, na którym sam bazowałem.
Warto przeczytać uważnie część Hurdles to Production Use, w której autor opisuje, dlaczego Angular Elements nie nadają się jeszcze do produkcyjnego użycia i kiedy może się to zmienić.
Vue w tej kwestii wyprzedziło Angulara i pozwala budować komponenty Vue jako Custom Element bezpośrednio z vue-cli. Wszystko działa jak należy i nie ma z tym problemów. Warto zaznaczyć, że w zbudowanym Custom Element nie ma pełnego Vue, co wiąże się z koniecznością zaimportowania biblioteki oddzielnie. Jednak dzięki temu podejściu komponenty są dużo mniejsze niż te Angularowe – to jest duży plus.
Stworzyłem dwa komponenty używając znanych przykładów:
- Hero App w Angularze,
- TODO List App w Vue.
Osadziłem oba komponenty na statycznej stronie HTML.
Nawet nie wiecie jakie było moje zdziwienie, gdy uruchomiłem Google Chrome i zobaczyłem, że to naprawdę działa 🙂
Jednak na tym niestety dobre wieści się kończą, ponieważ na żadnej innej przeglądarce strona nie działa.
Po dodaniu pollyfill’ów custom element stworzony we Vue zaczął działać w Firefoxie i Edge – tyle. Dla Angular’a uruchomienie zawsze kończyło się błędem:
TypeError: r.createShadowRoot is not a function
lub
SCRIPT438: Object doesn't support property or method 'createShadowRoot'
Szukając rozwiązania, odkryłem, że nie tylko ja mam ten problem i że aktualnie chyba nie ma dobrego rozwiązania.
Poza testowaniem na statycznej stronie HTML, spróbowałem dodać te komponenty do aplikacji napisanej w AngularJS. Komponent Vue zadziałał bez żadnych modyfikacji, z komponentem Angulara były problemy związane z podwójnym ładowaniem pliku zone.js. Jest na to workaround, ale chyba nie tędy droga 🙂
Co jeszcze na pewno powinno zostać przetestowane w naszym PoC?
- różne typy komunikacji między komponentami,
- compiler’y dla web component typu stencil,
- wydajność przy dużej ilości komponentów na jednej stronie.
Podsumowanie
Jak zwykle w pytaniach typu “Czy w swoim projekcie powinienem zastosować ten wzorzec/technologie?” najlepszą odpowiedzią jest – wszystko zależy od kontekstu. Architektura systemu jest dobrze zaprojektowana wtedy, gdy system spełnia postawione przed nim wymagania.
Podejście Micro Frontends jest na pewno warte rozważenia przy naprawdę dużych, skomplikowanych aplikacjach, nad którymi jednocześnie pracuje kilka zespołów odpowiedzialnych za różne obszary biznesowe.
Zastosowanie takiego podejścia rodzi pewne problemy, które nie występują przy monolitycznym frontendzie – dodatkowe osoby odpowiedzialne za integrację całości, trudności w ustalaniu globalnych standardów, błędy w projektowaniu mogące powodować dużo boleśniejsze skutki niż w przypadku monolitu.
Wydaje mi się, że aktualnie tylko najwięksi gracze, których aplikacje rozwijane są przez kilkanaście zespołów produktowych, zyskają dzięki zastosowaniu Micro Frontends. Jednak każdy z nas powinien zwracać uwagę, aby iść w stronę modularnego monolitu – dzielić frontend na małe, reużywalne moduły współpracujące ze sobą.
Web Components są już standardem gotowym do użycia. Jednak wsparcie najpopularniejszych frameworków pozostawia jeszcze dużo do życzenia.
W Angularze proces budowania nie jest wspierany z CLI, bundle są bardzo duże, ponieważ zawierają całego Angulara (ma się to zmienić po wprowadzeniu następnej generacji kompilatora), komponenty stworzone przeze mnie działały tylko na Google Chrome.
W React’ie nadal jest problem z bardziej zaawansowanymi funkcjami Custom Elements, więc pozwoliłem sobie pominąć go w testach. Może tutaj popełniłem błąd? Jeśli tak – będę wdzięczny za komentarze z krótkim wyjaśnieniem.
Najlepiej wypada Vue, który wspiera proces tworzenia Custom Elements z CLI, zbudowane bundle są małe i działają bez problemów na najpopularniejszych przeglądarkach.
Jeśli temat Was zainteresował to poniżej udostępniam listę ciekawych prezentacji, stron i blogów, z których sam korzystałem.
Slajdy/posty:
- Micro Frontend: a microservice architecture from your frontend web apps (slides)
- Micro Frontends: Break up your web app! (slides)
- Micro Frontends: building a modern web app with multiple teams (slides)
- Micro-Frontends.org
- Microservice websites by Gustaf Nilsson Kotte
- Micro Frontends List by Elisabeth Engel
- Front-end microservices with Web Components by Mikki Kobvel
Nagrania z prezentacji:
- Compositional UIs – the Microservices Last Mile – Jimmy Bogard
- microXchg 2018 – Micro Frontends – breaking down the last monolith – Matthias Laug
- Michael Geers – Micro Frontends: Break Up You Web App!
- Building web applications with Web Components by Martin Splitt
- The Future of Loading on the Web (Chrome Dev Summit 2017)
- code.talks 2017 – The Recipe For Scalable Frontends (Zalando)
- Micro Frontends Talks Playlist
Autor: Robert Witkowski, Senior Software Engineer, ASC LAB