Arian Skoki
Software Developer bei Carrot & Company.
Schreibt über Herausforderungen der Entwicklung.
8 Minuten

GraphQL und Angular: die richtige Option fürs State Management

8 Minuten
Artikel veröffentlicht am 20. Februar 2020
Was sind die Optionen, wenn ihr einen Local State wollt? Wie kann er mit Apollo integriert werden? Was für Möglichkeiten bringt Akita ins Spiel? Wir geben einen Überblick über die Möglichkeiten.

GraphQL wächst schnell, seit Facebook es 2015 Open Source zur Verfügung gestellt hat. Mit GraphQL kann man Serverdaten in der richtigen Form für die UI bereitstellen. Dadurch wird die Notwendigkeit eines State-Managements wie Redux minimiert. Außerdem braucht es auch keine Reducer, Selector, Actions etc. mehr. Dasselbe gilt für die Daten. Die Natur des Graphen erlaubt es, in die Tiefe zu gehen und alle benötigten related objects zur Response hinzuzufügen. Eine gute Erklärung des Themas findet sich im Blog-Post "How GraphQL Replaces Redux" von Mark Johnson .

Local State Management kann immer noch bei einigen komplexen Anwendungen wie der Verwaltung von Drafts und Forms, Pagination und Filter Settings helfen. Das wirft eine Frage auf: "Wie können wir mit dem Local State umgehen, wenn wir Apollo GraphQL verwenden?". Sollten wir den eingebauten Apollo-Cache für das Handling des Local State verwenden oder behalten wir NgRx wie in feedbackr ? Vielleicht sollten wir Loona, einem Wrapper über Apollo, eine Chance geben? Wenn sie nicht passen, sollten wir dann eine leichtgewichtige State-Handling-Library wie Ngxs oder Akita verwenden? Dieser Blog-Beitrag gibt einen Überblick über einige mögliche Lösungen.

Loona

Loona ist eine Open-Source-Library für das State-Management von The Guild . Aufbauend auf Apollo wurde sie entwickelt, um den gesamten State in einem gemeinsamen Store zu halten und gleichzeitig Developer-Experience zu verbessern. Sie verwendet die Logik der Actions und Effects von NgRx für eine bessere Trennung zwischen der Ausführung einer Mutation und der Aktualisierung des Stores.

Um typed Queries und Mutations zu ermöglichen, braucht es zuerst Boilerplate Code in Loona. Für jede Query oder Mutation ist ein zusätzlicher Schritt der Konvertierung in das Typescript-Interface erforderlich. Die Vorteile des GraphQL-Interfaces übertreffen den Overhead nicht. Da Loona ein ziemlich neues Open-Source-Projekt ist, verwenden es nicht viele Living Projects. Leider gab es in den letzten Monaten keine Aktivitäten auf dem Repository und es sieht so aus, als ob The Guild einen Schritt zurück gemacht hat. Sie haben zwar nicht gesagt, dass sie Loona nicht mehr unterstützen werden, aber sie haben auch nicht gesagt, dass sie es weiterführen werden.

Apollo-Client

Apollo's Cache ermöglicht es, lokale Daten mittels GraphQL zu verwalten. Es können sowohl lokale als auch remote Daten im Store sein und, was noch wichtiger ist, man kann sie in der gleichen Query abrufen. Der größte Vorteil der Verwendung des Apollo-Caches als lokaler Store ist, dass er zu einer einzigen Source of Truth wird.

Leider braucht es für die Verwendung des lokalen Stores etwas Overhead beim Schreiben von Query- und Mutationsdefinitionen. Außerdem sind die Vorteile des stark typisierten Mechanismus von Typescript nicht mehr nutzbar, da eine Query auf den lokalen Store als einfacher GraphQL-Literal-String geschrieben wird, siehe Graphql-tag Library .

Es besteht die Möglichkeit, ein GraphQL-Schema auf der Client-Seite zu schreiben, aber dieses Schema wird nicht zur Validierung, sondern nur zur Validierung in DevTools verwendet. Apollo ist vielleicht nicht die beste Wahl, wenn viele primitive Werte zum Einsatz kommen, aber es könnte in Situationen helfen, in denen komplexe Datenstrukturen lokal gespeichert werden müssen.

Redux-basierte Lösungen

Wenn Loona nicht überzeugt und das Schreiben von GraphQL zum Local State Management nicht zufrieden stellt, könnte eine der redux-basierten Lösungen helfen. Sie bieten eine "natürlichere" Handhabung des States. Gleichzeitig ist eine Synchronisierung zwischen Apollo und redux-basiertem Speicher erforderlich. Im nächsten Abschnitt werden wir NgRx , Ngxs und Akita vorstellen.

NgRx

NgRx war die erste Redux-Implementierung, die für Angular verfügbar war. Der Support für das Redux-Devtools-Plugin bietet eine Liste der jüngsten Actions mit ihren Auswirkungen auf den State. Es ist möglich, zu einer bestimmten Action zu springen und sogar einige zu überspringen. Entwickelnde können eine Action auch direkt aus den DevTools heraus senden.

NgRx behandelt Zustandsänderungen mit Hilfe von Observables, die ein Chaining mehrerer Selectors ermöglichen. Mit Effects lassen sich asynchrone Aktionen ausführen und von der UI isolieren. Ein Nachteil von NgRx ist, dass man immer noch eine Menge Boilerplate schreiben muss. Wie hier behauptet , wurde es nicht mit der Einstellung entwickelt, den Code auf dem schnellsten Weg zu schreiben. Dementsprechend steil ist auch die Lernkurve. Die NgRx-Community ist groß und wächst weiter, aber das Framework ist streng an Angular gekoppelt.

Ngxs

Die State Management Library Ngxs ist nur für Angular verfügbar. Die Unterstützung für Redux DevTools bietet nicht alle Funktionalitäten wie NgRx. Es ist nicht möglich, zu bestimmten Actions zu springen, einige zu überspringen oder neue mit den DevTools zu disptachen. Ngxs verwendet Funktionen innerhalb der State Class. Diese Funktionen können verkettet werden, aber es ist nicht so gut strukturiert wie in NgRx.

Nichtsdestotrotz hat Ngxs viele eingebaute Funktionen, einschließlich Offline-Persistence, Snapshot-Selection, Forms Synchronization, WebSockets und weiteren. Wie der Name schon sagt, lag der Schwerpunkt auf der Minimierung von NgRx-Boilerplates, die für einige Anwendungsfälle einfach zu groß sind. Die Unterstützung für DevTools mag zwar schlechter sein, aber die Reduzierung der Boilerplates, zusammen mit einer Vielzahl von Funktionen und einer gesunden Community rund um die Bibliothek gleichen diese Nachteile aus.

Akita

Akita wird von Datorama supportet und maintaint und kann mit Angular, React, Vie, Svelte oder sogar Vanilla JS verwendet werden. Da Akita auf RxJS aufgebaut wurde, ist die Funktionalität in den DevTools begrenzt. Actions und ihre Auswirkungen sind sichtbar und man kann zur jeweiligen Action in der Timeline springen, aber es unterstützt kein Live-Skipping oder Dispatching in den DevTools.

Akita fördert die Einfachheit durch die Verwendung objektorientierter Prinzipien. Es gibt keine asynchronen Actions, da die Subscription des Datenstroms manuell gehandhabt werden muss. Die Menge an Boilerplate Code ist jedoch gering, der größte Vorteil gegenüber den anderen Lösungen. Die Dokumentation ist ziemlich schön und die Entwickelnden von Datorama schreiben oft Blog-Posts über die Verwendung in verschiedenen Anwendungen.

Die Community ist nicht so groß und die eingebauten Funktionen sind nicht so umfangreich wie bei NgRx und Ngxs, aber der Vorteil liegt in der Einfachheit und der geringen Menge an Boilerplate. Das ist unserer Meinung nach etwas, das nicht ignoriert werden kann. Deshalb fiel unsere Wahl auch auf Akita.

Fazit

Es gibt verschiedene Optionen zur Implementierung des State Managements. Wir wollten lediglich einen Überblick über die vielversprechendsten Optionen geben. Für unseren Anwendungsfall war Akita die geeignetste Lösung, weil es nur sehr wenig Boilerplate einführt und die Lernkurve schneller als die anderen ist. Seine Unterstützung ist stabil, was für die langfristige Entwicklung wichtig ist.

Akita ist auch nicht Framework-abhängig, aber das macht es nicht zur besten Lösung für jedes Projekt. Alles in allem hat jede Option ihre Vor- und Nachteile, sodass sich letzten Endes alle für die beste Lösung für ihren Anwendungsfall entscheiden müssen.

Braucht ihr Hilfe bei der Entscheidungsfindung?

Wir verwenden Cookies 🍪