Zum Inhalt springen

Next.js und Sage. Vielleicht gibt es nichts aufzurufen.

Mehrere Sage-Produkte laufen auf einer Maschine im Büro des Kunden, ohne öffentliche Adresse. Das ist kein Hindernis für die Integration - es ist die Integration, und bestimmt, was eine Seite zeigt.

7 Min. Lesezeit

Es kommt eine Anforderung, das Kundenportal solle pro Konto die offenen Rechnungen zeigen, und die Rechnungen liegen in Sage. Das liest sich wie eine Aufgabe zum Datenabruf. Bevor es eine ist, gibt es eine Frage zu beantworten, die die gesamte Architektur entscheidet, und die Antwort lautet häufig, dass es keine Adresse zum Abrufen gibt.

Sage ist eine Produktfamilie und kein Produkt. Manche davon sind gehostete Dienste mit öffentlicher Schnittstelle und verhalten sich wie jede andere SaaS-Anbindung. Andere sind Anwendungen, die auf einem Server im Büro des Kunden installiert sind, und ihre Daten liegen in einer Datei auf diesem Server. Eine deployte Next.js-Anwendung erreicht diese Datei nicht, weder aus einer Server-Komponente noch von sonst wo, und keine passende Bibliothek ändert das.

Klären Sie zuerst, wo es läuft

Die erste Stunde des Projekts ist ein Telefonat, kein Schema.

Fragen Sie, welches Sage-Produkt, welche Edition und wo es installiert ist. Fragen Sie, wer diese Maschine administriert. Rechnen Sie damit, dass die Antwort unsicher ist, und rechnen Sie damit, dass "das ist in der Cloud" bedeutet, dass die Buchhaltung über den Browser auf einen Windows-Desktop zugreift - was sich für alle Beteiligten nach Cloud anfühlt und keine öffentliche API ist.

Es gibt nur zwei Ausgänge, und sie sind keine Varianten voneinander. Entweder es gibt eine Schnittstelle im Internet, dann haben Sie eine gewöhnliche Integration und die eigentliche Arbeit ist, wo welches Stück davon laufen sollte. Oder es gibt sie nicht, dann ist der nächste Abschnitt das ganze Projekt.

Die einzige Form, die genehmigt wird

Liegt das System im Netzwerk eines anderen, wird die Verbindung von innen nach außen aufgebaut. Etwas, das neben den Sage-Daten läuft - ein Dienst auf deren Server, ein geplanter Export, ein kleiner Konnektor -, schickt Daten über HTTPS nach außen an einen Endpunkt, den Sie kontrollieren. Nichts Eingehendes wird geöffnet, und deshalb ist das die Variante, die eine IT-Abteilung abzeichnet.

Ihre Next.js-Anwendung steht am anderen Ende davon, und sie ist nie das, was Sage aufruft. Sie empfängt, oder üblicher: Sie liest schlicht eine Datenbank, in die der empfangende Prozess schreibt.

Sage auf deren Server
  -> Konnektor in deren Netzwerk   (nur ausgehend)
    -> Ingest-Endpunkt             (prüft, schreibt)
      -> Ihre Datenbank            (das Lesemodell)
        -> Server-Komponenten      (rendern)

Dieses Bild in der ersten Woche zu zeichnen, ist wichtiger, als es aussieht, denn es ist zugleich der Vertrag. Alles links von Ihrer Datenbank ist eine Maschine, die Sie nicht kontrollieren, mit einem Zeitplan, den Sie nicht setzen, auf einem Server, den jemand über Weihnachten ausschaltet. Alles rechts davon gehört Ihnen und kann so schnell und so verlässlich werden, wie Sie mögen.

Sobald das Bild auf dem Papier ist, hört der Rest des Entwurfs auf, von Sage zu handeln. Es ist ein Lesemodell, und damit die gewöhnliche Aufgabe, eine Datenbank zu wählen und sie für die Abfragen Ihrer Seiten zu indizieren.

Das Alter der Daten ist ein Feld, keine Cache-Einstellung

Der Fehler, der nun folgt, ist, Aktualität als Caching-Frage zu behandeln. Das ist sie nicht, und die Unterscheidung lohnt Präzision, weil sich das Vokabular überschneidet.

use cache mit cacheLife steuert, wie lange Next.js ein berechnetes Ergebnis behält, bevor es neu berechnet wird. Das ist eine Aussage über Ihr eigenes Rendern. Es sagt überhaupt nichts darüber, wie alt die zugrunde liegenden Zahlen sind, denn das Neuberechnen liest dieselbe Datenbankzeile, die der Konnektor heute früh um halb sieben zuletzt geschrieben hat. Stellen Sie die kürzeste Cache-Lebensdauer ein, die Sie mögen, und der Rechnungsbetrag wird davon nicht neuer.

Das Alter der Daten muss also als Daten modelliert werden. Jede synchronisierte Entität trägt den Zeitstempel des Laufs, der sie erzeugt hat, und diese Spalte ist keine Diagnose - sie ist ein Feld, das die Oberfläche rendert:

export async function AccountBalance({ accountId }: { accountId: string }) {
  const account = await db.account.findUnique({ where: { id: accountId } })
  const age = Date.now() - account.syncedAt.getTime()
 
  return (
    <section>
      <Amount value={account.balance} />
      {age > 2 * 60 * 60 * 1000 ? (
        <Warning>
          Zuletzt aktualisiert {formatRelative(account.syncedAt)}. Das
          Buchhaltungssystem hat sich seither nicht gemeldet.
        </Warning>
      ) : (
        <Note>Stand {formatTime(account.syncedAt)}</Note>
      )}
    </section>
  )
}

Zwei Stunden sind keine magische Zahl, und Sie sollten Ihre eigene daraus ableiten, wie oft der Konnektor laufen soll. Der Punkt ist, dass die Komponente drei Zustände hat statt zwei, und der dritte - vorhanden, aber alt - ist der Zustand, in dem das System seine schlechten Tage tatsächlich verbringt. Eine Seite, die einen vierzehn Stunden alten Saldo genauso rendert wie einen frischen, ist kein Caching-Fehler. Sie ist ein Entwurf, der diesen Fall nie bedacht hat.

Manche Zahlen dürfen gar nicht veraltet gezeigt werden

Wenn entschieden ist, dass die meisten Werte mit ihrem Alter gezeigt werden dürfen, entscheiden Sie getrennt, welche nicht.

Ein offener Saldo von heute früh ist nützlich und klar beschriftet. Ein Kreditlimit, das darüber entscheidet, ob eine Bestellung angenommen werden darf, ist eine andere Art Zahl, und ein veralteter Wert bedeutet, eine Bestellung anzunehmen, die die Buchhaltung abgelehnt hätte. Vertragspreise sind dasselbe. Verfügbarer Bestand ebenfalls, wenn jemand ihn gerade einem Kunden zusagen will.

Dafür sagt die ehrliche Oberfläche, dass die Prüfung gerade nicht möglich ist, und bietet den nächsten Schritt an - die Bestellung zur Prüfung zurückhalten, oder dem Kunden sagen, dass sich jemand meldet. Das ist eine schlechtere Erfahrung als eine Zahl und eine bessere als eine falsche, und die Buchhaltung sagt Ihnen in etwa zehn Minuten, welche Felder hierher gehören, wenn Sie direkt fragen.

Zurückschreiben ist ein anderes Versprechen

Lesen ist die leichte Richtung. Ein Formular in Ihrer Anwendung, das auf der anderen Seite etwas anlegt, ist die Stelle, an der die Architektur geprüft wird, weil Sie es nicht bestätigen können.

Die Server Action schreibt die Anfrage in Ihre eigene Datenbank und kehrt zurück. Mehr kann sie wahrheitsgemäß nicht tun. Ob der Datensatz Sage erreicht, hängt an einem Konnektorlauf, der Minuten entfernt sein kann, und die Action hat keine Möglichkeit, darauf zu warten, ohne eine Anfrage länger offen zu halten, als irgendeine Plattform erlaubt.

Was bedeutet: Ausstehend ist ein echter Zustand mit eigener Zeile, eigenem Bildschirm und eigenem Fehlerpfad, kein Spinner. Der Benutzer sendet ab, sieht, dass es eingereiht wurde, und sieht es auf bestätigt wechseln, wenn eine spätere Synchronisation zurückmeldet. Scheitert es an der Validierung auf der Sage-Seite

  • ein Debitorenkonto, das es nicht gibt, ein umbenanntes Sachkonto -, muss dieser Fehler irgendwo landen, wo ein Mensch hinsieht, denn es gibt keine Anfrage mehr, an die man einen Fehler zurückgeben könnte.

Das als optimistische Oberfläche zu bauen und zu hoffen, ist die mit Abstand häufigste Art, wie diese Projekte im ersten Monat nach dem Start eine Woche verlieren. Der Schreibvorgang hat stattgefunden oder nicht, und die Anwendung ist das Einzige, was den Unterschied halten kann.

Was dabei herauskommt

Nichts davon ist ein Behelf für eine Einschränkung. Ein so gebautes Lesemodell ist schneller als jeder Live-Aufruf es gewesen wäre, steht, wenn der Buchhaltungsserver nicht steht, verbindet sich mit Ihren übrigen Daten und lässt sich für die Abfragen indizieren, die Ihre Seiten tatsächlich stellen - statt für die, für die eine Buchhaltungs-API entworfen wurde.

Was es kostet, ist die Ehrlichkeit: Die Zahlen haben ein Alter, die Oberfläche sagt es, und Schreibvorgänge sind Anfragen und keine Tatsachen. Teams, die das von Anfang an einbauen, liefern ein Portal, dem Leute glauben. Teams, die es nach dem ersten Support-Ticket nachrüsten, schreiben genau die Komponenten neu, auf die es am meisten ankommt.

Unsere Enterprise-Projekte haben weitgehend diese Form, und die Grenze zwischen Anwendung und führendem System ist der Teil, den wir zuerst zuschneiden.

Verwandte Fragen

Kann eine Server-Komponente Sage direkt aufrufen?
Nur wenn das fragliche Sage-Produkt eines der gehosteten mit öffentlicher Schnittstelle ist. Bei den Desktop- und On-Premise-Editionen gibt es keine Adresse, die Ihre deployte Anwendung auflösen könnte - die Frage ist also nicht, ob es gute Praxis ist, sondern dass der Aufruf gar nicht stattfinden kann. Herauszufinden, in welcher Lage Sie sind, ist die erste Aufgabe im Projekt.
Können wir nicht einfach einen Port in deren Firewall öffnen lassen?
Fragen können Sie, und die Antwort der meisten IT-Abteilungen ist zu Recht nein. Ein Buchhaltungsserver im Internet ist ein Risiko mit dem gesamten Hauptbuch dahinter. Üblich ist das Gegenteil: Etwas in deren Netzwerk baut eine ausgehende Verbindung zu Ihnen auf, was keine eingehende Regel braucht und sehr viel leichter genehmigt wird.
Wie aktuell werden die Daten sein?
So aktuell wie der letzte erfolgreiche Lauf dessen, was synchronisiert, und der hängt an einer Maschine, die Sie nicht administrieren. Minuten, wenn es durchläuft und der Server gesund ist; ein Tag, wenn es nachts läuft; länger, sobald jemand den Server neu startet und den Dienst vergisst. Entwerfen Sie die Oberfläche für den schlechten Fall, nicht für den guten.
Heißt das, das Projekt lohnt sich nicht?
Nein, es heißt, der Nutzen liegt woanders als in einer Live-Ansicht. Ein Lesemodell aus einem Buchhaltungssystem gibt Ihnen schnelle Seiten, eigene Indizes, Daten, die sich mit dem Rest Ihrer Anwendung verbinden lassen, und ein System, das steht, wenn deren System nicht steht. Das ist real, und nichts davon verlangt, dass die Zahl auf dem Schirm eine Sekunde alt ist.

Zurück zu allen Artikeln