Przejdź do treści
Wszystkie usługi
Usługa 03 / 03 Dostępny · part-time · B2B

Architektura mikrofrontendów

Od 2021 roku pracuję przy platformie z około 28 aplikacjami Vue w single-spa, które komunikują się ze sobą w czasie rzeczywistym.

Współpraca
Dostępny · part-time · B2B
Stack
Vue 3 · single-spa · TypeScript · GraphQL · Apollo · SignalR · Storybook · Kubernetes · Azure

Mikrofrontendy to narzędzie, nie cel

Mikrofrontendy mają sens wtedy, gdy kilka zespołów pracuje nad różnymi częściami jednego produktu i musi móc wdrażać je niezależnie. Znacznie mniej sensu mają przy jednym zespole i jednej aplikacji. W takim przypadku dobrze zaprojektowany monolit jest zwykle prostszy w budowie, łatwiejszy w utrzymaniu i tańszy w uruchomieniu.

Dlatego od tego zaczynam: jaki problem mają rozwiązać mikrofrontendy? Jeśli nie chodzi o niezależność zespołów, możliwość osobnego wdrażania albo wyraźny podział odpowiedzialności, prostsze rozwiązanie może być lepszym wyborem.

Praca z platformą mikrofrontendową od 2021 roku

Od 2021 roku współpracuję z zespołem frontendowym pracującym nad platformą B2B2C dla firmy cateringowej w Niemczech. To długofalowa współpraca, która z czasem rozrosła się do dużego ekosystemu frontendowego z około 28 aplikacjami Vue działającymi pod single-spa.

Aplikacje wykorzystują różne technologie komunikacji i dostępu do danych, zależnie od potrzeb: GraphQL z Apollo, API REST oraz SignalR do aktualizacji w czasie rzeczywistym. Pracowałem również nad środowiskiem opartym na Dockerze i Kubernetesie, pipeline'ami CI/CD w Azure oraz wspólnymi narzędziami używanymi przez całą platformę.

Praca nad tym samym systemem przez kilka lat daje inną perspektywę niż zbudowanie proof of concept. Widać, które granice rzeczywiście się sprawdzają, gdzie zespoły zaczynają tworzyć ukryte zależności i które elementy architektury wymagają mocniejszych zasad.

Gdzie naprawdę pojawia się złożoność

Granice i typowane kontrakty

W architekturze mikrofrontendowej najtrudniejsze rzadko jest samo uruchomienie kolejnej aplikacji. Trudniejsze jest ustalenie, za co każda aplikacja odpowiada, co może wiedzieć o pozostałych i w jaki sposób się z nimi komunikuje.

Dbam o to, żeby te granice były wyraźne, a kontrakty widoczne w kodzie dzięki TypeScriptowi. Dotyczy to zarówno współdzielonych pakietów, jak i komunikacji między aplikacjami. Jeśli jedna aplikacja ma zareagować na zdarzenie pochodzące z innej, format tej wiadomości traktuję jak kontrakt API, a nie ustalenie, które istnieje tylko w głowach członków zespołu.

Wspólna biblioteka komponentów

Przy około 28 aplikacjach pozwolenie każdemu zespołowi na niezależne rozwiązywanie tych samych problemów UI szybko prowadzi do niespójności. Dlatego zaprojektowałem i wdrożyłem dla platformy bibliotekę komponentów Vue w Storybooku, która daje zespołom wspólny zestaw komponentów i wzorców.

Celem nie było centralizowanie każdej decyzji. Chodziło o to, żeby wspólne problemy rozwiązać raz, dobrze je udokumentować i ułatwić ich ponowne wykorzystanie.

Więcej kontekstu znajduje się w case study platformy mikrofrontendowej.

Niezależne środowiska i wdrożenia

Niezależne aplikacje potrzebują również niezależnych środowisk pracy i sposobu wdrażania. Na tej platformie, za pomocą Dockera i Kubernetesa, tworzyłem izolowane środowiska dla poszczególnych feature'ów zamiast jednego wspólnego środowiska dla wszystkich developerów.

Pracowałem również nad pipeline'ami CI/CD w Azure obsługującymi wdrażanie mikrofrontendów. Kiedy aplikacji jest wiele i każda może być wdrażana osobno, sposób dostarczania oprogramowania staje się częścią architektury. To właśnie dobre środowisko lokalne i przewidywalny pipeline sprawiają, że niezależność aplikacji jest praktyczna na co dzień.

Komunikacja między aplikacjami

Aplikacje komunikują się przez event bus zbudowany na window.dispatchEvent. Sam browserowy mechanizm jest prosty. Ważniejsze jest to, jak korzysta z niego cały system.

Opakowałem event bus w dedykowany pakiet SDK, z którego korzystają wszystkie aplikacje. Dzięki temu platforma ma jeden, typowany interfejs komunikacji zamiast sytuacji, w której każdy zespół wymyśla własne nazwy zdarzeń i formaty payloadów. Kiedy kontrakt się zmienia, TypeScript pokazuje nam aplikacje, które trzeba dostosować.

To jeden produkt i jeden zespół, ale architektura musi działać w skali około 28 niezależnie wdrażanych aplikacji. Platforma single-spa, wspólna biblioteka komponentów, środowiska Kubernetes oraz narzędzia do tłumaczeń są częścią tego samego systemu.

Dane, cache i aktualizacje w czasie rzeczywistym

Kiedy wiele aplikacji korzysta z tych samych danych backendowych, szybko pojawia się problem nieaktualnego albo zduplikowanego stanu. Jedna zmiana może wtedy uruchomić kilka niepotrzebnych requestów albo pozostawić różne części interfejsu z różnymi wersjami tych samych danych.

Przy pokrewnym przepisaniu aplikacji na Vue 3 dla tego samego klienta pracowałem nad typowaną warstwą danych i celowaną inwalidacją cache. Zamiast odświeżać wszystko po każdym zdarzeniu real-time, wiadomość z SignalR może unieważnić tylko te zapytania, których rzeczywiście dotyczy zmiana.

To podejście dobrze sprawdza się również w środowisku mikrofrontendowym: jasno określać odpowiedzialność, kontrolować przepływ danych i nie odświeżać większej części stanu, niż faktycznie się zmieniła. Opisałem to szerzej w case study dotyczącym wydajności.

Jak utrzymać spójność dużego frontendu

Przy wielu aplikacjach architektury nie da się zdefiniować raz i potem o niej zapomnieć. Małe skróty zaczynają się kumulować, a granica, którą wystarczająco często się ignoruje, w końcu przestaje być granicą.

Dlatego platforma opiera się zarówno na regułach technicznych, jak i na codziennej pracy zespołu.

  • Linting i hooki. Ścisłe reguły lintowania, formatowanie i git hooki wyłapują wiele problemów ze spójnością, zanim trafią do code review.
  • Code review. Review jest szczególnie ważne przy rzeczach, które łatwo przeoczyć w izolacji, np. imporcie tworzącym niezamierzoną zależność między aplikacjami.
  • Prowadzenie zespołu i mentoring. Prowadzę zespół frontendowy pracujący nad platformą i mentoruję developerów pracujących z tą architekturą, dzięki czemu wiedza i decyzje nie zostają tylko u jednej osoby.

Jak podchodzę do współpracy przy mikrofrontendach

Zaczynam od problemu, a nie od architektury.

Czy naprawdę potrzebujesz mikrofrontendów, czy ten sam problem da się rozwiązać za pomocą modularnego monolitu i mniejszej ilości złożoności? Jeśli mikrofrontendy mają uzasadnienie, kolejnym krokiem jest określenie granic aplikacji, kontraktów komunikacyjnych i wspólnych elementów systemu.

Potem trzeba sprawić, żeby te decyzje działały w praktyce: najważniejsze reguły warto egzekwować narzędziami, środowiska developerskie i deployment powinny wspierać niezależną pracę, a wspólna infrastruktura powinna być na tyle dobrze przygotowana, żeby zespoły nie musiały za każdym razem tworzyć własnych rozwiązań.

Celem nie jest stworzenie efektownej architektury. Celem jest system, który można bezpiecznie zmieniać, niezależnie wdrażać i nadal rozumieć rok czy dwa lata później.

Powiązane obszary

Platformy mikrofrontendowe często ujawniają również inne problemy frontendowe. Wolne zapytanie albo zduplikowany fetch może być powielany przez wiele aplikacji, a starsze fragmenty platformy mogą z czasem zacząć ograniczać rozwój pozostałych.

Dlatego zajmuję się również audytami wydajności frontendu oraz migracjami z Vue 2 do Vue 3, często jako częścią większej pracy nad platformą, a nie jako odizolowanymi projektami.

Jestem dostępny part-time, w modelu B2B, zdalnie. Opisz swoją platformę i zespoły, a odpowiem w ciągu jednego dnia roboczego.

Przyjrzyjmy się Twojemu frontendowi.

Part-time, B2B, zdalnie. Odpowiadam w ciągu jednego dnia roboczego.

Porozmawiajmy