Weil wir (noch) kein GraphQL haben

In einer glücklichen Zukunft werden wir einfach etwas wie GraphQL nutzen, um Datenanfragen dynamisch vom Server zusammenzustellen und dabei nicht mehr und nicht weniger abzurufen, als wir benötigen. Aber dieser Tag ist noch nicht gekommen.

In der Zwischenzeit müssen wir Ressourcen über dieselbe API anfordern, die auch unsere Kunden nutzen, wobei diese manchmal Begleitdaten enthält und manchmal nicht.

Nehmen wir zum Beispiel unseren „Job“-Endpunkt, wie er in Thrift dargestellt ist:

Tatsächlich enthält eine Anfrage an die API nach Auftragsdaten Arrays mit Auftragsbenutzern und Dokumenten und verweist lediglich über Fremdschlüssel auf Auftragsvorlagen. Lässt man den geschäftlichen Kontext eines Auftrags einmal außer Acht, kann man sich eine einfache Liste dieser Aufträge in einer Seitenleiste vorstellen, deren Auswahl dann im Hauptfenster in den Fokus rückt.

Erste Iteration: Props in React übergeben

Als wir uns zum ersten Mal daran machten, eine Benutzeroberfläche rund um dieses Konzept zu entwickeln, überließen wir React einen Großteil der Datenverwaltung und gaben Daten – manchmal sogar ziemlich viele – als Props an untergeordnete Komponenten weiter.

Machen wir mit dem Beispiel einer Liste von Stellenangeboten weiter (linke Seitenleiste oben). Hier ist ein relativ anständiger erster Entwurf:

Die wichtigsten Punkte, die es zu beachten gilt, sind:

  • Die übergeordnete Komponente erhält als Props den aktuell ausgewählten Auftrag, eine Liste aller Aufträge sowie ein „idObjectCache“, bei dem es sich lediglich um einen manuell gepflegten Speicherort für Nachschlageinformationen handelt.
  • Das übergeordnete Element gibt alle Informationen an das untergeordnete Element weiter.
  • Datenaufbereitung in Form von verketteten Underscore-Funktionen innerhalb von `getRows ` (Entschuldigung, ich meinte natürlich Lodash. Entschuldigung an alle.)
  • Eine kleine, alberne Suchfunktion im untergeordneten Element namens „templateIdFromName“

Dies ist zwar ein relativ überschaubares Beispiel, doch bei komplexeren Bereichen der App, in denen wir sehr unterschiedliche Datenquellen miteinander verknüpfen müssen, gerät die Situation schnell außer Kontrolle.

Hinzu kommt, dass sich im Job-Modell das vollständige Team-Modell verbirgt, das wir im obigen Code verwenden (this.props.job.team.name), aber nie für andere Komponenten abstrahieren. Auf derselben Seitenansicht führen wir außerdem einen API-Aufruf für das Team des aktuellen Jobs durch.

Zweite Iteration: Abrufen von Fremdschlüsseldaten in NuclearJS

Das ist kein Geheimnis. Ich bin total begeistert von NuclearJS. Hier ist ein erster grober Versuch, Alt zugunsten meines neuen Lieblingsprojekts aufzugeben:

Die Unterschiede sind subtil, und ihre Bedeutung ist möglicherweise nicht auf den ersten Blick erkennbar, wenn man React [noch] nicht in der Praxis eingesetzt hat.

  • Der übergeordnete Container nutzt nun die Getter von Nuclear, um unveränderliche Datenlisten abzurufen, wobei alle Filter- und Sortiervorgänge bereits erledigt sind.
  • Die einzigen Parameter, die die Komponente erhält, beziehen sich auf den UI-Status (z. B. „Jobs werden abgerufen“), doch sie verwaltet den Abruf ihrer Daten intern selbst. Das bedeutet, dass die Komponente und ihre übergeordnete Komponente sicher umgestaltet oder verschoben werden können.
  • Das „Job“- Modul ist ein allgemeines Modul, das wir in zwei verschiedenen Apps verwenden, aber „currentJobId“ ist spezifisch für die WebEx-Anwendung (Web Execution – bitte entschuldigen Sie den internen Fachjargon) und somit für deren eigenes Modul.
  • Für das Abrufen tiefer verschachtelter Werte kommen wir mit der wesentlich sichereren Methode `getIn() ` aus.

Das Ergebnis ist wesentlich übersichtlicher und für unsere Designer viel einfacher zu verstehen, zu gestalten und anzupassen. Großartig, aber wie sieht es mit dem Abruf der Daten aus?

Fremdschlüsseldaten verwalten, wie es Jason Bourne tun würde

Die Nuclear-Dokumente bieten eine hervorragende Strategie zur Erstellung von API-Aktionen Modell für Modell. Da der von uns um Thrift herum geschriebene Wrapper Promises zurückgibt, sind viele unserer Modellimplementierungen einfache Zuweisungen:

exports.fetchAll = Thrift.enhanced.JobRole.index;

Wenn wir jedoch die Fremdschlüsseldaten der Rückgabe verwalten möchten, können wir unsere Gegner im Handumdrehen ausschalten, ganz wie Jason Bourne, und zwar folgendermaßen:

Anmerkung: An einigen Stellen im obigen Code könnten wir uns etwas prägnanter ausdrücken, aber zumindest ist für den nächsten Entwickler (so hoffe ich zumindest!) völlig klar, was hier vor sich geht.

Die API-Antwort abfangen

Der erste Trick besteht darin, dass die Aktion je nach den von der Benutzeroberfläche (d. h. einer React-Komponente) übermittelten Parametern unterschiedliche API-Endpunkte verwendet. Da sowohl die Thrift- als auch die Nuclear-Funktion Promises zurückgeben, können wir uns dazwischen schalten und das resultierende Jobs-Array an `processForeignKeys` übergeben.

Abfrage der erforderlichen Fremdschlüsseldaten

Der zweite Trick besteht darin, eine eindeutige Liste von Jobvorlagen-IDs aus der Jobliste abzurufen (bei mehr als 50 Jobs gibt es möglicherweise nur 3–4 Vorlagen). Ein Indexaufruf für die Vorlagen müsste paginiert werden; statt also alle Daten abzurufen, können wir Anfragen nur für das stellen, was wir tatsächlich benötigen. Sollte der Index-Endpunkt jemals den Empfang eines Arrays von IDs unterstützen, könnten wir diese in einem einzigen Aufruf übermitteln.

Natürlich handelt es sich hierbei um „Fire-and-Forget“-Aktionen. Unsere Getter aktualisieren die React-Komponenten kostenlos, sobald die Fremdschlüsseldaten eintreffen. Einfach genial.

Einbetten von Fremdschlüsseldaten

Der dritte Trick besteht darin, dass wir die Daten zu Benutzern, Jobrollen und Teams aus verschiedenen Jobs übernehmen und über eine einfache „receiveAll“-Funktion laden können, die wir erstellt haben:

Bei jedem Modell tut die Funktion „receiveAll“ lediglich so, als würde sie eine von ihr selbst initiierte API-Abfrage bearbeiten. Das Ergebnis ist dasselbe.

Vielleicht ist Ihnen oben auch aufgefallen, dass wir bei der Verarbeitung doppelter Fremdschlüsseldaten über verschiedene Jobs hinweg die Funktion `uniq()` so umfunktionieren, dass lediglich die IDs verglichen werden. Das ist wesentlich einfacher als ein umfassender Vergleich. Allerdings beruht dies auf unserer Zuversicht, dass der Server uns in derselben Anfrage keine unterschiedlichen Daten mit denselben IDs sendet. Wenn wir uns darauf nicht verlassen können, haben wir größere Probleme.

Die letzte Etappe: Getters

Du hast es bis hierher geschafft. Jetzt bringen wir den Football in die Endzone.

Das Format der Nuclear-Getter wirkt auf den ersten Blick etwas überladen, ist aber großartig.

  • The raw job data itself is stored as an Immutable Map of String<jobId> to Map<job>
  • Die `jobList` überwacht Daten zu Jobs und Jobvorlagen, führt Abfragen durch, sobald Vorlagen verfügbar werden, und gibt die Ergebnisse als unveränderliche Liste zurück.
  • Die Exporte „activeJobList“ und „completedJobList“ wenden lediglich Filter und Sortierungen an, wie man sie aus jeder Sprache bzw. jedem Framework kennt. Dabei erhalten Sie ganz nebenbei eine einseitige Datenbindung.

Letztendlich haben wir…

  • Unser gesamter API-Code in einem Thrift-Wrapper
  • Alle unsere API-Daten werden als unveränderliche Objekte gespeichert und durchgehend von Nuclear verwaltet, einschließlich der Abhängigkeiten bei Fremdschlüsseldaten.
  • Unsere gesamte Benutzeroberfläche in React, einschließlich Datenabruf und Datenbindung, ist so weit wie möglich lokalisiert.

…was so einfach ist, dass ich dadurch Zeit habe, einen Medium-Beitrag darüber zu schreiben.


Wenn du das hier hilfreich fandest, zögere nicht, es weiterzuempfehlen.

Ach ja, und wir suchen gerade unseren dritten Front-End-Entwickler – falls du Lust hast, bei einem Start-up, das mit einer Serie-A-Finanzierung ausgestattet ist und unter anderem von Andreessen Horowitz, First Round, Kleiner Perkins und Google Ventures unterstützt wird, gegen Bezahlung mit React, Nuclear und ImmutableJS zu experimentieren. Melde dich bei mir.