Disegnare un'applicazione web sfruttando il micro-frontend pattern

di Morgan Pizzini, in HTML5,

Perchè spendere tempo a imparare vari framework frontend, trovarne i punti forti e quelli deboli, se poi all'atto pratico siamo costretti a utilizzarne uno, farci esperienza, scrivere componenti che vanno a sopperire alle mancanze e creare un prodotto finale che possiamo poi riutilizzare in futuro per un qualsiasi nuovo progetto, dato che ormai ci fidiamo della piattaforma scelta? È tramite questo processo che nascono gruppi di lavoro e a volte società completamente dedicate a un framework. Non è certo raro sentire risposte come "Con Angular di può fare tutto", "Abbiamo creato un framework che sta sopra Vue.js", "Utilizziamo librerie React che ci permettono di svolgere tutto quello che ti occorre". Di certo la specializzazione non è un problema, infatti questa porta a un innegabile incremento della produttività del singolo a discapito, forse, della testabilità o della comprensione del codice. Un'applicazione che al suo interno contiene varie sezioni, più o meno complesse, è senza ombra di dubbio difficile da testare e al tempo stesso l'onboarding di nuovi colleghi risulta complicato e richiederà molto tempo. Per comprendere queste difficoltà con un esempio possiamo immaginare la struttura di un applicativo complesso come può essere lo shopping di Amazon o la libreria Netflix, la fase di testing è fondamentale per ogni singola sezione dell'applicazione, in quanto verrà utilizzata da milioni di utenti e deve essere, per questo motivo, testata in ogni sua parte e affidabile al 100%, sono inoltre società con molti dipendenti e la conoscenza della piattaforma deve richiedere il minimo sforzo possibile.

Micro frontend: cosa sono?

A questo scopo nascono i micro frontend: una rielaborazione del concetto di micro services adattata al mondo del frontend. È la possibilità di suddividere la pagina in varie sezioni, ognuna gestita da un'applicazione diversa. Contestualizzando con Angular: i componenti di alto livello, come header, navbar, footer, content, verranno sostituiti da applicazioni che ne gestiranno il contenuto autonomamente. È altresì innegabile che questa suddivisione aumenti esponenzialmente il livello di complessità dell'applicazione, caratteristiche che potrebbero svogliare dalla scelta nel caso in cui fossimo in una realtà medio-piccola. Ma come nel caso dei microservices, adottati in primis da queste società e poi arrivati a noi, anche per il mondo del frontend si stà espandendo questa metodologia di lavoro.

In cosa consiste l'innovazione? Come anticipato possiamo creare varie applicazioni a sé stanti per ogni sezione della pagina, queste possono essere costruite con lo stesso framework o anche variare: avere l'header scritto in React, il contenuto con Angular e il footer in Vue.js, scegliendo quindi l'opzione più adatta a ogni circostanza. Tale partizionamento necessita la creazione di una shell: un'applicazione che a seconda delle circostanze carica i componenti adatti.

Storia micro frontend

Dal 2018 Angular introduce la possibilità di creare gli Angular elements, essi non sono altro che componenti stand-alone che possono essere iniettati in una pagina esistente attraverso l'utilizzo di un tag HTML personalizzato e lo script generato dalla compilazione. Questo porta Angular ad avere le stesse potenzialità e poter essere utilizzato per lo stesso scopo per cui le altre librerie sono state create: sostituire parzialmente una parte dell'applicativo. La migrazione progressiva al framework/libreria consente di operare modifiche direttamente in produzione e permettere, a porting completato, di utilizzare il framework al 100% delle sue potenzialità.

Con i micro frontend questo discorso si interrompe uno step prima dell'unificazione. Si è notato che testare i vari componenti singolarmente risulti molto più facile se suddivisi in applicazioni indipendenti, anche lo sviluppo ne tra vantaggio nel momento in cui abbiamo due team, specializzati su tecnologie diverse: entrambi possono lavorare nel loro "habitat", con i loro strumenti e librerie per ottenere il miglior risultato possibile.

Esempi di implementazione

Data l'etereogenità che può avere l'infrastruttura, per l'esempio corrente scegliamo di affidarci ad Angular come shell, principalmente perchè a differenza di Vue e React è pensato per essere utilizzato nella sua interezza, per riuscire quindi ad avere un pacchetto utilizzabile in altri progetti abbiamo bisogno di creare un Angular element. Una volta compresa la modalità di sviluppo a micro frontend, le stesse tecniche possono essere declinate in qualsiasi framework o libreria esistente. Il risultato finale di ogni singola compilazione può essere definito Web Component: uno standard HTML usato per definire un componente HTML in grado di operare all'interno di un qualsiasi framework frontend.

Quella proposta in questo articolo è solo una delle tanti modi in cui possiamo comporre una pagina basata sui micro frontend: prenderemo ogni singolo script e lo caricheremo nel DOM. Altre procedure possono caricare la pagina basandosi su vari iframe, uno per ogni componente. Scegliamo inoltre un approccio di separazione per sezione: ogni elemento sarà una sezione della pagina (header/footer/list/nav), altri esempi possono essere la suddivisione per pagine o per funzionalità, nella prima ogni web component sarà una pagina (homepage/profilo/carrello) mentre nella seconda troveremo un insieme di pagine (account/e-commerce/impostazioni).

Angular elements

Per poter creare un web component partendo da un'applicazione Angular basterà lanciare un comando e modificare poche righe:

# applicazione
ng new mia-app
cd mia-app
ng add @angular/elements

# libreria
ng new mia-app
cd mia-app
ng generate library mia-lib
ng add @angular/elements --project=mia-lib

Modifichiamo il modulo principale imponendo la creazione di un custom element specificando il componente da caricare e il tag in cui inserirlo

import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { BrowserModule } from '@angular/platform-browser';

@NgModule({
  declarations: [
    MioComponent
  ],
  imports: [
    BrowserModule
  ]
  entryComponents: [MioComponent]
})
export class AppModule {
  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    const customElement = createCustomElement(MioComponent, { injector: this.injector });
    customElements.define('mio-component', customElement);
  }
}

Possiamo ora buildare l'applicazione

# applicazione
ng build --prod
#libreria
ng build mia-lib --prod

Verranno creati i file main.js, polyfills.js, runtime.js e styles.css con i relativi hash, li andiamo a referenziare all'interno di un qualsiasi HTML nel seguente modo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="dist/mia-app/styles.css">
    <script src="dist/mia-app/mia-app.js"></script>
</head>
<body>
    <mio-component></'mio-component>
</body>
</html>

Dato che importare 3 diversi file, con relativo hash, può dare a problemi di typo, e nel caso di componenti multipli potremmo avere potenzialmente più file omonimi differenziati solo dai caratteri di hash, usando lo script seguente, e associandolo a un comando all'interno del package.json, siamo in grado di unificare tutti gli script in un unico file e nominarlo a nostro piacimento

cat ./dist/mia-app/{runtime,polyfills,main}-es5.js > dist/mia-app/mia-app.js
2 pagine in totale: 1 2
Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

Top Ten Articoli

Articoli via e-mail

Iscriviti alla nostra newsletter nuoviarticoli per ricevere via e-mail le notifiche!

In primo piano

I più letti di oggi

In evidenza

Misc