Moderne Frontend-Architekturen fĂŒr Single-Page-Anwendungen

Oliver Zeigermann / @DJCordhose /

Slides: https://djcordhose.github.io/spa-workshop/2020_arch.html

Wir nehmen Frontend-Architektur nicht fĂŒr voll

Zitate

  • Frontend-Architektur? Ich dachte sowas gibt es gar nicht
  • Das Frontend kloppen wir am Ende einfach irgendwie drauf
  • Architektur fĂŒr ein bisschen CSS-Pixel-Geschubse?
  • Von Frontend habe ich keine Ahnung - muss also leicht sein! (das ist paraphrasiert, ist so nicht gefallen)

Prominentes Beispiel fĂŒr Client Side Include: Spotify mobile App

Dies gilt nur fĂŒr die App, der Web Player ist seit einiger Zeit als React-Monolith gebaut: http://labs.spotify.com/2019/03/25/building-spotifys-new-web-player/

Architekturbedingte UX-Probleme

Apps nur gleichzeitig dargestellt, aber nicht komplett integriert

Prominentes Beispiel fĂŒr Reine Vertikalen: Outlook Online

Architekturbedingte UX-SchwÀchen

unterschiedliche Technologien, jede App wird anders dargestellt
Wechsel der App dauert (Service Workers können die Zeit ab 2. Aufruf vermindern)
Umstellung auf React fortlaufend

Prominentes Beispiel fĂŒr SPA Deployment-Monolith: Google Docs

Keine architekturbedingten UX Probleme

Beispiel

Was wĂ€ren Kandidaten fĂŒr Smart-/Dumb-Components?

Dumb

Smart

Grenzen von Smart/Dumb

Besonders bei wachsenden und langlebigen Anwendungen

  • Tendenz zu "Gottkomponenten": Zustand und Logik wandern langsam nach oben in eine einzige Komponente
  • Vermischung von Framework und UI-Logik (erschwert Austausch das Frameworks und das Testing)
  • Verteilter, verĂ€nderlicher Zustand erschwert Wartbarkeit
    • Zustand oft nicht klar zuzuordnen
    • In welchem Zustand ist die Anwendung?
  • Architektur immer noch unklar
    • Wo ist NebenlĂ€ufigkeit erlaubt?
    • Wie lĂ€uft die Initialisierungsphase
    • Wie testet man die Business Logik?

Redux

  • Zentrale Zustandsverwaltung: ein Speicher (Store) fĂŒr die gesamte Anwendung, wie eine Datenbank
  • Externe Zustandsverwaltung: Extrahieren von Logik und Zustand aus den (UI-)Komponenten
Nicht nur ein Framework, auch ein Pattern (wie MVC)

Redux

Architektonische Richtlinien

  • Uni-direktionaler Datenfluss
  • Externer, zentraler und unverĂ€nderlicher Zustand: Store
  • Nur reducer dĂŒrfen den Zustand verĂ€ndern
  • Zustand wandert von Smart Components zum Store
  • UI-Logik wandert von Smart Components in Action-Creators / Services und Reducer
  • Asynchroner Code nur in Action-Creators / Services oder Effects
  • Initialisierung der Anwendung mit zentraler Aktion

Ein Beispiel

Welche Daten braucht man minimal zur Darstellung?

Application State

Local UI State

Views

Redux ist unabhÀngig vom Framework

Implementierungen existieren fĂŒr alle wichtigen UI-Frameworks

Code Sample: Reducer

Nur eine Funktion, daher unabhÀngig vom UI Framework


type Greeting = {
	greeting: string;
	name: string;
}
            

type SaveGreetingAction = {
	type: 'ADD_GREETING',
	greeting: Greeting
}
            

function greetingsReducer(state: Greeting[] = [],
                          action: SaveGreetingAction) {
    switch (action.type) {
        case 'ADD_GREETING':
            // immutable operation, creating new state
            return [...state, action.greeting];
        default:
            return state;
    }
}
            

Code Sample: Action Creator

Ebenso unabhÀngig vom UI Framework


async function loadGreetings(dispatch) {
    try {
        const response = await fetch(BACKEND_URL);
        const json = await response.json();
        dispatch({
            type: SET_GREETINGS,
            greetings: json
        });
    } catch (err) {
        console.error('LOADING GREETINGS FAILED:', err)
    }
}
            

Wieder nur eine Funktion, der einzige Ort an dem asynchrone Operationen erlaubt sind

Ist das nur eine spinnige Idee, oder nutzt das echt jemand?

Microsoft Outlook, Twitter, Apple, XING and many others use React and Redux

Wrap Up Redux

Ein UI-Muster fĂŒr Benutzerschnittstellen

  • Mainstream-Lösung
  • unabhĂ€ngig vom SPA-Framework
  • Einfaches Testen der GeschĂ€ftslogik, da die Logik nur in reinen Funktionen implementiert ist ("Reducer")
  • Großartiges Debugging wegen der Entwicklungswerkzeuge
  • Funktioniert hervorragend in großen Anwendungen mit vielen AbhĂ€ngigkeiten zwischen Teilen/Komponenten
  • Bietet eine architektonische Anleitung, wohin welcher Teil der Anwendung geht

Warum Typensysteme verwenden?

Ein Typsystem macht den Code einfacher zu warten

  • kann Code besser lesbar machen
  • kann Code leichter analysierbar machen
  • kann zuverlĂ€ssiges Refactoring ermöglichen
  • kann eine allgemein bessere IDE-UnterstĂŒtzung ermöglichen
  • kann einige (typbezogene) Fehler frĂŒhzeitig erkennen

Anders Hejlsberg@Build2016: Big JavaScript codebases tend to become "read-only".

JavaScript flavors

https://2019.stateofjs.com/javascript-flavors/

Basics


// variables can have type information
let foo: string;
foo = 'yo';
// Error: Type 'number' is not assignable to type 'string'.
foo = 10;


// types can be inferred (return type)
function sayIt(what: string) {
  return `Saying: ${what}`;
}
const said: string = sayIt(obj);


class Sayer {
  what: string; // mandatory

  constructor(what: string) {
      this.what = what;
  }

  // return type if you want to
  sayIt(): string {
      return `Saying: ${this.what}`;
  }
}
https://www.typescriptlang.org/