Anzeige
Anzeige

Den Datenfluss in Web-Apps besser im Griff: Der Flux-Kompensator

Mit der zunehmenden Dominanz von JavaScript in der Browser-App-Entwicklung nimmt die Komplexität im Frontend-Code rapide zu. Auch etablierte Frameworks und Bibliotheken können nicht verhindern, dass oft der Überblick über Ursache und Wirkung im Event-Dschungel verloren geht. Mit Flux macht nun ein Architekturmuster von sich reden, welches die landläufige Überzeugung infrage stellt, MVC und Two-Way-Data-Binding seien der Weisheit letzter Schluss. Zurecht.

9 Min.
Artikel merken
Anzeige
Anzeige

Die Idee und der Entwurf für Flux wurden im Mai 2014 von Facebook-Entwicklern vorgelegt. Das Architekturmuster ist die Antwort darauf, wie sich Facebook zukünftig mit dem ebenfalls haus-gebackenen Framework React (siehe Ausgabe 41) vorstellt, komplexe und somit potenziell unüberschaubare Datenflüsse in Web-Applikationen abbilden zu wollen [1].

Anzeige
Anzeige

In Demos ist es immer ganz einfach: Eine Benutzereingabe erzeugt ein neues Element in einem JavaScript-Array, das dem Benutzer automatisch und formschön als aktualisierte To-do-Liste präsentiert wird. Der Vorfall ist fertig implementiert. Nicht so bei Facebook – dessen Abhängigkeiten in der Geschäfts- und Darstellungslogik sind weitaus komplexer. Auch wenn man keine sozialen Netzwerke baut: Die viel bemühte To-do-Liste entspricht in der Realität eher einer Liste von Produkten eines Onlineshops, mit Abhängigkeiten zum Warenkorb, ähnlichen Suchanfragen und mehr. Jeder UI-Entwickler, der mit derartigen Beziehungen zu tun hat, wird früher oder später feststellen, dass der Code schnell an Struktur und Übersichtlichkeit verliert: Abhängigkeiten werden angesichts eines fehlenden Entwurfs-Musters an ungeeigneten Controller- oder Router-Stellen implementiert, die Nachvollziehbarkeit der Ereignis-Behandlung geht verloren. Durch die Erzeugung marginal angepasster Kopien entsteht redundanter und wartungsintensiver Code.

Flux wurde also aus der Motivation heraus geboren, die Veränderung und den Fluss von Daten im Browser in eine wohldefinierte und nachvollziehbare Ablaufstruktur zu bringen. Flux wird manchmal fälschlicherweise in den exklusiven Zusammenhang mit React gebracht, doch tatsächlich beschreibt das Architekturmuster ganz allgemein ein Modell für Zuständigkeiten und Datenflüsse in JavaScript-Client-Architekturen.

Anzeige
Anzeige

Undirektionaler Datenfluss

Flux stellt als alternatives Architekturmuster zu MVC und der oft eingesetzten bidirektionalen Datenbindung einen Entwurf für den unidirektionalen Datenfluss vor. Es separiert strikt die Verarbeitung und somit Veränderung von Daten von ihrer Repräsentation. Es ergänzt das ebenfalls aus dem Hause Facebook stammende Framework React, ist aber auch losgelöst davon für die Integration in andere Frameworks und Umgebungen geeignet. Facebook selbst hat eine einfache Referenz-Bibliothek veröffentlicht. Was genau verbirgt sich hinter dem „Flux-Pattern“ und in welchen Fällen lohnt es sich, diese Architektur für das eigene Projekt zu evaluieren und einzusetzen?

Anzeige
Anzeige

Die Ausgangslage

Nehmen wir zunächst noch einmal die typische Verarbeitungsweise von Client-Architekturen nach dem MVC- beziehungsweise MVVM-Muster unter die Lupe. Als gedankliches Beispiel dient wieder die Suchfunktion eines Onlineshops: Die Formulareingabe eines Besuchers wird von einem Event-Handler entgegengenommen, mittels einer Controller-Funktion validiert und in eine geeignete Anfrage an das zugrunde liegende Modell umgewandelt, entweder durch die Ausführung eines Server-Requests oder durch die Filterung einer bereits im Browser geladenen Collection von Produkt-Modellen. Im einfachsten Fall landen die zurückgelieferten Suchergebnisse dann direkt in einer Variable der View, sodass sich diese mit den neuen Daten rendern lassen.

Eine klassische MVC-Architektur birgt das Risiko unübersichtlicher Aktualisierungs-Kaskaden zwischen Modellen und Views.
Eine klassische MVC-Architektur birgt das Risiko unübersichtlicher Aktualisierungs-Kaskaden zwischen Modellen und Views.

Soweit ein Interface wenige und in ihren Rollen weitgehend isolierte Modelle und Views abbilden soll, ist ein MVC-Muster ein einfacher und effektiver Weg zur Umsetzung. Oft ist das Beziehungsnetz von UI-Komponenten jedoch weitaus komplexer: Benutzer-Interaktionen haben Auswirkungen auf den Zustand anderer Modelle oder Modell-Aggregationen der Domäne, sodass verschiedene View-Komponenten der gesamten Darstellung einer Aktualisierung bedürfen. Ein wichtiger Aspekt ist hierbei, dass es sich um Beziehungen in den Präsentationsformen handelt, welche oft von der Geschäftslogik des zugrunde liegenden Datenbank-Modells abstrahieren. Um im Beispiel der Shop-Suche zu bleiben, denke man zum Beispiel an die Einblendung der letzten Suchanfragen oder Produktempfehlungen auf Basis des zuletzt gesuchten Produkts („Das könnte Sie auch interessieren…“).

Anzeige
Anzeige

Die Herausforderung ist nun, diese Quer-Beziehungen abzubilden, ohne dass der Gewinn an Wiederverwendbarkeit durch eine Separation von Darstellung, Verarbeitungslogik und Modell verloren geht. Hier klafft bei der Definition der MVC-Architektur eine Lücke und es fehlt an geeigneten Ausdrucksmitteln. Zudem ist die in vielen populären JavaScript-Frameworks eingesetzte Technik des „Two-Way-Data-Bindings“ zwar komfortabel und spart eine Menge Code, kann aber auch problematisch sein: Bei impliziter Bindung von View-Variablen an ein Objekt, welches seinerseits überwacht wird und Aktualisierungen an anderen Objekten durchführt, lassen sich Ursache und Wirkung von Daten-Mutationen manchmal schwer nachvollziehen. Im Resultat sieht man sich einer unerwünschten Kopplung zwischen verschiedenen Modellen und View-Komponenten gegenüber:

  • View- bzw. Controller-Komponenten werden über ihre eigentlichen Zuständigkeiten, nämlich die Beschreibung ihrer eigenen Darstellungslogik hinweg aufgebläht. Der Code wird komplizierter und ist aufwändiger zu warten und zu testen.
  • Sobald mehrere Modelle und Views sich gegenseitig bedingen, wird unter Umständen eine Kaskade von Ereignissen, Funktionsaufrufen und Aktualisierungen ausgelöst, welche schwer zurückzuverfolgen ist und im Extremfall sogar zu zirkulären Abhängigkeiten („Totschleifen“) führen kann. Im Folgenden Code ist beispielsweise ein überladener Event-Handler für die Shop-Suche in einer View-Komponente implementiert. Abhängigkeiten erfordern zusätzliche Modell-Aktualisierungen und View-Renderings.
Event-Handler Shop-Suche in einer View-Komponente
var handleSearchTermSubmit = function() {
  var term = this.searchField.value.trim();
  Products.fetch(term, function(products) {
    this.state.searchResults = products;
    this.render();
    this.SearchHistoryView.state.items.push(term);
    this.StatusView.state.message = 'Hits: ' + products.length;
  });
  Recommendations.fetch(term, function(products) {
    this.RecommendationsView.state.products = products;
    // [...]
  });
};

Listing 1

Die meisten JavaScript-Frameworks bieten eigene „Best Practices“ an, um diesen Problemen beispielsweise mit Beobachtern (Observern) und Regeln für die Event-Delegationen zwischen Parent- und Child-Views zu begegnen. Zusammengefasst: Ein komplexes Web-Interface nach einem MVC-Entwurf mitsamt eines nachvollziehbaren Ereignis-Systems zu implementieren, ist keine unlösbare Aufgabe, erfordert aber viel Disziplin und gedankliche Vorarbeit. Man wird zwangsläufig Strategien entwickeln, um den Daten- und Kommunikationsfluss zwischen Komponenten zu vereinheitlichen. Aber genau diese Vorarbeit haben andere bereits geleistet.

Geordneter Datenfluss

An diesem Punkt kommt nun endlich Flux als Alternative ins Spiel, welches ein Modell für den unidirektionalen Datenfluss und eine strikte Separation von Aktions-Verarbeitung, Daten-Manipulation und Daten-Repräsentation definiert.

Anzeige
Anzeige

Die Flux-Architektur sieht neben den bereits bekannten und in ihrer Rolle nur leicht veränderten Views vor allem „Actions“, einen „Dispatcher“ und „Stores“ als neue Komponenten vor. Der Datenfluss folgt dabei immer dem folgenden Verarbeitungsweg:

Action → Dispatcher → Store → View (→ Action …)

Alle Ereignisse, welche Einfluss auf den Zustand der Applikation nehmen, werden als Action definiert und über den Dispatcher verteilt, der als zentraler Event-Hub agiert. Die Stores bilden den Applikations-Zustand ab und stellen die Verarbeitungs-Logik zur Verfügung. Im Gegensatz zum „Model“ aus dem MVC-Muster sind Stores per Definition nicht darauf beschränkt, dedizierte Objekttypen zu repräsentieren. Sie können jede Art von Daten abbilden und verarbeiten, welche zu der von ihnen behandelten Teil-Domäne passen. Die reine Flux-Lehre sieht für die Erzeugung einer Action semantische Hilfsmethoden vor, bezeichnet als Action-Creators.

Anzeige
Anzeige

Dies soll mit Hilfe des Shop-Beispiels illustriert werden. Der Event-Handler für das Abschicken der Suchanfrage ruft einen Action-Creator nach dem „Fire & Forget“-Prinzip auf: Es gibt keine direkten Rückgabewerte und es werden auch keine Callback-Funktionen für eine etwaige Ergebnisverarbeitung übergeben. Stattdessen werden durch den Action-Creator eine oder mehrere Actions mitsamt Nutzdaten für den Dispatcher erzeugt. Bezogen auf die Shop-Suche könnte eine Action wie folgt definiert sein:

Definition Action
var actions = {
  SEARCHTERM_SUBMITTED: 'SEARCHTERM_SUBMITTED'
};
dispatch(SEARCHTERM_SUBMITTED, { term: 'sphero' });

Listing 2

Der Name einer Action wird im Flux-Kontext als Konstante definiert und sollte ein bereits erfolgtes Ereignis beschreiben. Der Dispatcher meldet diese Action nun an alle Stores, sodass diese darauf reagieren und ihren Zustand aktualisieren können. Im Beispiel der Shop-Suche könnte ein Store „Products“ sowohl die Suchergebnisse als auch die empfohlenen Produkte verwalten, aber genauso gut kann sich eine Aufteilung in einen Store „ProductSearch“ und einen Store „ProductWarehouse“ anbieten: Diese würden nun unabhängig voneinander die Action ,SEARCHTERM_SUBMITTED‘ verarbeiten und adäquate Suchergebnisse bereitstellen. Nach der Verarbeitung melden die Stores „Change-Events“, um Veränderungen an den Daten bekannt zu geben. Als letzten Schritt schließlich rendern alle Views, welche Daten von einem oder mehreren Stores als State binden, ihre DOM-Repräsentation neu. Ein Action-Dispatch-Zyklus ist damit abgeschlossen.

Flux vereinfacht die Nachvollziehbarkeit von Datenänderungen durch das Konzept des unidirektionalen Datenflusses. Daten bewegen sich immer in eine Richtung durch die zuständigen Teile des Systems und haben deshalb zu jedem Zeitpunkt fest definierte Zustände.
Flux vereinfacht die Nachvollziehbarkeit von Datenänderungen durch das Konzept des unidirektionalen Datenflusses. Daten bewegen sich immer in eine Richtung durch die zuständigen Teile des Systems und haben deshalb zu jedem Zeitpunkt fest definierte Zustände.

Der Datenfluss kann als Kreisverkehr betrachtet werden: Daten bewegen sich stets in eine Richtung durch die zuständigen Teile der Architektur. Dies bedeutet, dass das System zu jedem Zeitpunkt definierte Zustände besitzt und eindeutige Zustandswechsel durchläuft, die sich reproduzieren und somit gut testen lassen.

Anzeige
Anzeige

Flux ist in seiner Grundidee kein gänzlich neuer Entwurf. Parallelen hat es etwa zu dem durch Young/Fowler beschriebenen Muster CQRS (Command-Query Responsibility Segregation) [2]. CQRS setzt im Kern auf eine klare Trennung von Modellen beziehungsweise Objekten zur Manipulation und Objekten zur Abfrage von Daten. Wichtig ist dabei, dass nie gleichzeitig eine Mutation und eine Rückgabe von Daten über ein und dasselbe Objekt beziehungsweise die darin ausgeführte Methode stattfinden darf. Flux unterscheidet zwar nicht explizit zwischen Kommandos und Abfragen, aber diese Trennung ergibt sich automatisch aus der Isolierung in Actions und dem davon entkoppelten Rückfluss von veränderten Daten über die Change-Events der Stores.

Traumpaar: React + Flux

Das ebenfalls bei Facebook aus der Taufe gehobene Framework React setzt den Schwerpunkt in der Kapselung des Zustands, der Logik und Lebenszyklus-Definition von View-Komponenten. Wer bereits die ersten Schritte mit React unternommen hat, wird wissen, dass Konzepte und Prototypen für Modelle und deren Datenbindung fehlen. So stellt sich bei der Entwicklung mit React nicht nur die Frage nach den richtigen Bibliotheken für zum Beispiel Routing und Server-Kommunikation, sondern auch für die Modellierung von Datenstrukturen und Datenflüssen – hier stellt Flux eine passende Komplementärarchitektur zu den React-Views dar.

Das erweiterte Flux-Muster im Kontext von React. Auch hier gilt: eindeutiger Datenfluss in eine Richtung erleichtern Wartbarkeit und Testing des Systems.
Das erweiterte Flux-Muster im Kontext von React. Auch hier gilt: eindeutiger Datenfluss in eine Richtung erleichtern Wartbarkeit und Testing des Systems.

Die Stores füllen die Lücke zur Abbildung der Modelle und Geschäftslogik. Views auf Kompositions-Level, so genannte „Controller-Views“, empfangen Daten der Stores und reichen diese selektiv als Properties an ihre Kind-Komponenten weiter. Das React-Prinzip „re-render everything“ bei Zustandsänderungen fügt sich nahtlos in den Flux-Kreislauf ein: Am Ende eines Zyklus werden sämtliche Views, welche den veränderten Zustand repräsentieren, automatisch neu gerendert. Das React-Team empfiehlt übrigens, die Server-Kommunikation innerhalb der Action-Creator durchzuführen: Nach einem Ajax-Request an eine API sollte eine Action mit einer Server-Antwort als Datenrückgabe erzeugt werden. Im Shop-Beispiel könnte diese etwa „PRODUCTFETCH_SUCCEEDED“ lauten, die zu übergebenden Nutzdaten bestünden aus einer Liste der gefundenen Produkte. Auch die Action „PRODUCTFETCH_FAILED“ ist denkbar, die Payload wäre dann die Fehlermeldung, welche ebenfalls nach Flux-Manier von einem oder mehreren Stores verarbeitet und über View-Bindungen dargestellt wird.

Anzeige
Anzeige

Immutability & Isomorphie

Zwei interessante Aspekte sollen an dieser Stelle noch Erwähnung finden. Zum einen das Konzept der „Immutability“: Hiermit ist das Paradigma gemeint, nach welchem Daten nie direkt mutiert werden, sondern sich immer nur als veränderte Kopie speichern lassen. Dies wird nicht nur der Lehre der reinen funktionalen Programmierung gerecht, sondern birgt gleichzeitig unschätzbare Vorteile wie eine „kostenlose Undo-History“ und drastisch verbesserte Testbarkeit. Das Projekt redux genießt diesbezüglich derzeit große Aufmerksamkeit und schlägt eine von Flux inspirierte Architektur kombiniert mit eingebauter Immutability vor [3].

Auch der Begriff der „Isomorphen Web-Anwendung“ taucht vermehrt auf. Als isomorph werden JavaScript-Anwendungen bezeichnet, die ganz oder teilweise client- und serverseitig ausgeführt werden können. Serverseitiges Rendering kann wichtig für SEO-Zwecke, aber auch bei Performance- oder UX-Problemen durch viele kleinteilige und damit kostspielige Rendering-Vorgänge im Browser sein. Bei React etwa ist es leicht möglich, Views komplett serverseitig in einer Node.js-Umgebung rendern zu lassen. Da Flux mit dem unidirektionalen Datenfluss die Zuständigkeiten der einzelnen Anwendungs-Teile gut separiert, bringt es eine der Grundvoraussetzungen für die Erstellung isomorphen Codes mit sich. Das Projekt fluxible stellt einen „Container“ mit Werkzeugen für die Implementierung isomorpher Flux-Anwendungen bereit und bietet auch Tools zur Bindung an React [4].

Fazit

Wer Web-Applikationen mit komplexen Beziehungen in der Präsentations-Logik entwickelt, sollte auf jeden Fall eine Flux-Architektur in Betracht ziehen. Insbesondere, wenn die Repräsentation und Veränderung von Daten an verschiedenen Stellen gleichzeitig und auf unterschiedlichen Wegen erfolgen kann, ermöglicht sie eine signifikante Vereinfachung in der Entwicklung und schließlich auch Test- und Wartbarkeit des Codes. Die individuelle Auslegung des Musters kann dabei variieren und es stehen eine Vielzahl quelloffener und gut dokumentierter Bibliotheken zur Verfügung, um Flux zum eigenen Projekt passend zu adaptieren [5].

Anzeige
Anzeige

Die Tatsache, dass die Idee für Flux den Entwickler-Köpfen aus dem Hause Facebook entspringt, bedeutet nicht, dass sich Flux nur sinnvoll im Zusammenhang mit React einsetzen ließe. Dennoch passt die Flux-Architektur besonders gut zum reaktiven Ansatz der React-Views. Selbst wenn Flux eines Tages in der jetzigen Variante überholt sein sollte: Es hat bereits den Stein ins Rollen gebracht, gewohnte Muster wie MVC und Derivate als Universalmuster in der Web-Entwicklung zu überdenken.

Mehr zu diesem Thema
Fast fertig!

Bitte klicke auf den Link in der Bestätigungsmail, um deine Anmeldung abzuschließen.

Du willst noch weitere Infos zum Newsletter? Jetzt mehr erfahren

Anzeige
Anzeige
Kommentare

Community-Richtlinien

Bitte schalte deinen Adblocker für t3n.de aus!
Hallo und herzlich willkommen bei t3n!

Bitte schalte deinen Adblocker für t3n.de aus, um diesen Artikel zu lesen.

Wir sind ein unabhängiger Publisher mit einem Team von mehr als 75 fantastischen Menschen, aber ohne riesigen Konzern im Rücken. Banner und ähnliche Werbemittel sind für unsere Finanzierung sehr wichtig.

Schon jetzt und im Namen der gesamten t3n-Crew: vielen Dank für deine Unterstützung! 🙌

Deine t3n-Crew

Anleitung zur Deaktivierung
Artikel merken

Bitte melde dich an, um diesen Artikel in deiner persönlichen Merkliste auf t3n zu speichern.

Jetzt registrieren und merken

Du hast schon einen t3n-Account? Hier anmelden

oder
Auf Mastodon teilen

Gib die URL deiner Mastodon-Instanz ein, um den Artikel zu teilen.

Community-Richtlinien

Wir freuen uns über kontroverse Diskussionen, die gerne auch mal hitzig geführt werden dürfen. Beleidigende, grob anstößige, rassistische und strafrechtlich relevante Äußerungen und Beiträge tolerieren wir nicht. Bitte achte darauf, dass du keine Texte veröffentlichst, für die du keine ausdrückliche Erlaubnis des Urhebers hast. Ebenfalls nicht erlaubt ist der Missbrauch der Webangebote unter t3n.de als Werbeplattform. Die Nennung von Produktnamen, Herstellern, Dienstleistern und Websites ist nur dann zulässig, wenn damit nicht vorrangig der Zweck der Werbung verfolgt wird. Wir behalten uns vor, Beiträge, die diese Regeln verletzen, zu löschen und Accounts zeitweilig oder auf Dauer zu sperren.

Trotz all dieser notwendigen Regeln: Diskutiere kontrovers, sage anderen deine Meinung, trage mit weiterführenden Informationen zum Wissensaustausch bei, aber bleibe dabei fair und respektiere die Meinung anderer. Wir wünschen Dir viel Spaß mit den Webangeboten von t3n und freuen uns auf spannende Beiträge.

Dein t3n-Team

Kommentar abgeben

Melde dich an, um Kommentare schreiben und mit anderen Leser:innen und unseren Autor:innen diskutieren zu können.

Anmelden und kommentieren

Du hast noch keinen t3n-Account? Hier registrieren

Anzeige
Anzeige