Merkur aneb jak vzniká mikrofrontend framework

Mikrofrontendy jsou zajímavá adaptace konceptu microservices pro potřeby frontendového vývoje. Podívejte se, co to v praxi obnáší a proč jsme se v Seznam.cz rozhodli vyvinout náš vlastní microfrontend framework, Merkur.

Mikro-co?

Mikrofrontendy, jinak taky frontendové mikroslužby, jsme my frontendisté tak trochu odkoukali od backendu. Představa je asi taková:

  1. Frontend se rozdělí na samostatné uzavřené celky (komponenty, widgety),
  2. určí se způsob, jak budou komunikovat mezi sebou a s backendem,
  3. vytvoří se kompoziční vrstva, která složí všechny dílky dohromady tak, aby to všechno společně fungovalo.

Jestli se teď drbete na hlavě a ptáte se, k čemu je to všechno dobré, odpovědi jsou podobné jako u backendových mikroslužeb: třeba jednodušší odlaďování menšího balíku kódu, opakované použití prvků napříč projekty, nebo menší vývojové týmy. Na druhou stranu, mikroslužby nejsou samospásné a složitost, kterou s sebou nesou, nemusí vždy vyvážit jejich výhody.

O mikrofrontendech jsme u nás začali vážně uvažovat v momentě, kdy jsme začali pracovat na jednotné ovládací liště pro Seznamácké služby. Naše weby běží na různých platformách – React, IMA.js (izomorfní JavaScript framework postavený na Reactu, který můžete znát ze seriálu tady na Zdrojáku), nebo přímo čistý JavaScript. A to mluvíme jen o frontendu – backend je kapitola sama pro sebe. Samostatný modul, nezávislý na technologiích hostující aplikace, by nám značně zjednodušil život. 

Když jsme se začali rozhlížet po existujících možnostech, rychle jsme zjistili jednu věc: protože mikrofrontendy jsou spíš koncept než vývojový vzor, zdá se, že existuje sto a jedno řešení, jak je vytvářet. Dobrý přehled různých přístupů nabízí například Florian Rappl ve článku 6 Patterns for Microfrontends.

Při bližším pohledu se ale ukázalo, že většina frameworků nesplňuje naše potřeby. Nemůžeme použít nginx k embedování našich mikrofrontendů (jak to navrhuje Micro Frontends project), nemůžeme přejít na GCloud nebo Amazon S3 (OpenComponents), nemůžeme zcela rozdělit naše weby na mikrofrontendy (Piral). A u většiny ostatních pak chyběl server-side rendering, bez kterého se kvůli SEO a výkonnosti neobejdeme.

Nakonec jsme se rozhodli, že nejjednodušší bude postavit si řešení vlastní: Merkur.

Po vlastní ose

Merkur widget v akci můžete vidět na této ukázce. Chcete-li se podívat, jak to vypadá takříkajíc pod kapotou, použijte @merkur/create-widget, který generuje vše potřebné pro provoz Merkur widgetu. Nejjednodušší je spustit ho pomocí npx: npx @merkur/create-widget my-first-widget.

Co pro vás @merkur/create-widget nachystá? Merkur jako takový (@merkur/core), generování HTML na serveru i klientu (na výběr je několik knihoven jako Preact nebo Hyper) a nakonec obsluhu serverové části widgetu (v současnosti Express). Jak HTML renderer, tak server je možné vyměnit za jinou knihovnu – jestli chcete, můžete mít Merkur widget postavený na Vue a obsluhovaný třeba Fastify. 

Fungování Merkuru nejvíc připomíná to, co popisuje Zack Jackson ve svém článku Micro-frontend Architecture: Replacing a Monolith from the Inside Out. Vypadá to nějak takhle:

  1. Hostující aplikace zavolá Merkur widget API v průběhu vykreslování na serveru. Jako props předá všechny informace potřebné pro první vykreslení widgetu.
  2. Widget se inicializuje a provede své vlastní serverové vykreslení.
  3. Widget API vrátí widget jako JSON serializovaný objekt se všemi daty – názvem widgetu, číslem verze, props, state widgetu, a především vyrenderovaného HTML a odkazy na skripty a styly.
  4. Hostující aplikace zahrne skripty, styly a vyrenderované HTML do vygenerovaného kódu stránky.
  5. V prohlížeči se widget oživí. Protože jsou widgety pojmenované a verzované, je možné jich provozovat na jedné stránce víc zároveň.
  6. Widget teď běží v hostující aplikaci. Komunikace je možná prostřednictvím props widgetu (host => widget) a buď nativními DOM eventy, nebo Merkur eventy na widgetu (widget => host). 

Mimochodem, server-side rendering je volitelný. Stejně jako velká část Merkuru, když už jsme u toho; základ Merkuru je minimální, většinu funkcionality obstarávají pluginy.

Jako ukázka, definice jednoduchého Counter widgetu vypadá následovně

import { componentPlugin } from '@merkur/plugin-component';
import { eventEmitterPlugin } from '@merkur/plugin-event-emitter';
import { errorPlugin } from '@merkur/plugin-error';
import pkg from '../package.json';
import View from './component/View';

export const widgetProperties = {
  name: pkg.name,
  version: pkg.version,
  $plugins: [componentPlugin, eventEmitterPlugin, errorPlugin],
  View,
  assets: [
    {
      name: 'polyfill.js',
      type: 'script',
    },
    {
      name: 'widget.js',
      type: 'script',
    },
    {
      name: 'widget.css',
      type: 'stylesheet',
    },
  ],
  onClick(widget) {
    widget.setState({ counter: widget.state.counter + 1 });
  },
  onReset(widget) {
    widget.setState({ counter: 0 });
  },
  load(widget) {
    // We don't want to set environment into app state
    // eslint-disable-next-line no-unused-vars
    const { environment, ...restProps } = widget.props;

    return {
      counter: 0,
      ...restProps,
    };
  },
};

Zprávy z první linie

Do nynějška jsme vytvořili 5 widgetů o různé složitosti. Bude potřeba ještě nějaký čas na plné vyhodnocení všech pozitiv a negativ, ale minimálně z hlediska vývoje se s Merkurem pracuje příjemně. 

Zaprvé, widgety jsou malinké. V porovnání s kódem našich webů je příjemná změna hledat chybu v něčem, co má sotva 15 tisíc řádků – a to včetně samotné knihovny!

To také dává prostor k určitému experimentování. Samozřejmě s mírou; nedokážu si představit větší noční můru, než spravovat armádu widgetů, který každý běží na jiné technologii. Ale i tak jsme našli pár věcí k vyzkoušení. Ne všechno se promítne zpět do našich běžných služeb (i když se nám líbily React hooky, do současné codebase postavené na třídách to úplně nepasuje), ale některé určitě ano (přejít na Webpack je rozhodně v plánu).

Ne všechno bylo samozřejmě “sluníčkové”.

Postavit vlastní nástroj doopravdy znamená ho postavit – žádná dokumentace, žádné “best practices”, na všechno si musíte přijít sami. Nenarazili jsme naštěstí na žádný kritický problém, ale pár nepříjemných momentů jsme zažili.

Kromě věcí týkajících se přímo Merkuru jsme se potýkali i s problémy vycházejícími přímo z mikrofrontendů jako takových. Nevýhoda uzavřených jednotek je zvýšená komplexnost systému jako celku. Nebo jinak: jednotlivé díly jsou jednoduché, ale jejich propojení už tak úplně ne. Nejvíc se to projevuje při odlaďování chyb. Tam, kde byla doteď jen jedna aplikace, jejíž kód a chybové hlášky bylo potřeba sledovat, teď jsou dvě. Připočtěte k tomu několik vrstev integračních modulů (kterým se při použití na více webech nevyhnete) a o pár dlouhých večerů je postaráno.

Všeobecně vzato to ale vidíme optimisticky. Nějakým problémům se v začátcích projektu vyhnout nedá a těch pár, se kterými jsme se setkali, nás od mikrofrontendů neodradilo. Takže Merkur v Seznamu určitě plánujeme používat dál. 

Pokud vás Merkur zaujal, tak do budoucna plánujeme napsat něco o našem posledním projektu, vizualizačních widgetech pro senátní a krajské volby z října 2020. Mezitím podívejte na dokumentaci a GitHub – Merkur je OpenSource a spolupráci určitě vítáme. 

Zabývá se vývojem webů od roku 2014, v současnosti především v JavaScriptu. Od roku 2019 pracuje ve firmě Seznam.cz jako frontendová vývojářka, na obsahových webech jako SeznamZprávy a souvisejících interních projektech. 

Komentáře: 14

Přehled komentářů

Mlocik97 Definice
Peter V jednoduchosti je krasa
Slavek SSR?
L. Není framework jako framework
ja mikrofrontend na Seznamu
Mlocik97 To nesúhlasím
L.
Martin Proče ne typescript
Mlocik97 áno
Slavek
Slavek
Tonda Panděras Merkur je 100 let za opicema!
Slavek
Slavek
Zdroj: https://zdrojak.cz/?p=24295