To download the Sequential ID Generator, visit: [tulip](https://<span translate=).co/library/apps/sequential-id-generator---multiple-prefix-support">Library
In diesem Artikel erläutert Tamás, einer unserer Bibliotheksentwickler, die Entwicklungsentscheidungen hinter der in der Bibliothek verfügbaren Baustein-App „Sequential ID Generator“.
Warum ich es so entwickelt habe
Ich wollte, dass dieser Logikblock ein sehr praktisches Problem löst: IDs auf eine Weise zu generieren, die zuverlässig, leicht wiederverwendbar und schnell in vielen Apps einsetzbar ist.
Das wichtigste Designziel war nicht einfach nur „die nächste Zahl generieren“. Das eigentliche Ziel war es, etwas zu schaffen, das – einmal definiert – mit sehr geringem Aufwand kopiert und wiederverwendet werden kann. Wenn eine Schaltfläche einmal auf saubere Weise erstellt wurde, sollte es möglich sein, sie in eine andere App oder einen anderen Schritt einzufügen und lediglich die Konfiguration anzupassen. Ich wollte nicht, dass jeder App-Entwickler jedes Mal die Logik erneut öffnen und die Trigger neu schreiben muss.
Aus diesem Grund ist der Logikblock so konzipiert, dass die Konfiguration Vorrang vor der Logik hat. Wenn Sie diesen Logikblock verwenden, müssen Sie sich hauptsächlich Gedanken darüber machen:* welches Präfix benötigt wird* wie lang der numerische Teil sein soll* aus welcher Tabelle die Schaltfläche Daten lesen soll
Sobald Sie das Präfix, das Zahlenformat und die Tabelle definiert haben, sollten die Logikblöcke den Rest bereits übernehmen: das Auslesen der aktuellen ID, deren Validierung, die Generierung des nächsten Werts und die Überprüfung, ob die Zahl noch der konfigurierten Länge entspricht.
Die Tabelle selbst sollte die „Quelle der Wahrheit“ sein. Ein separater Zähler (beispielsweise eine Variable) kann von den tatsächlichen Daten abweichen, wenn Datensätze auf unerwartete Weise eingefügt, gelöscht oder bearbeitet werden. Durch das Auslesen aus der Tabelle bleibt die Nummerierung an die tatsächlich vorhandenen Daten gebunden.
Eine weitere wichtige Entscheidung war, die zentrale Berechnungslogik an einem Ort zu bündeln. Ich wollte nicht einen Triggerzweig für die erste ID, einen weiteren für die nächste ID und dann noch zusätzliche kleine Triggerfragmente, die später schwer nachzuvollziehen sind. Ich wollte, dass die Hauptlogik in einem einzigen „ expression “ untergebracht ist, damit das Verhalten konsistent bleibt und jeder, der sie später ändert, genau weiß, wo er nachsehen muss.
Der Logikblock basiert also im Wesentlichen auf einigen praktischen Überlegungen:* einfach zu kopieren und wiederzuverwenden* sicher genug, um die Tabelle vor fehlerhaften ID-Mustern zu schützen* einfach zu konfigurieren* zentralisierte Logik, damit die Wartung überschaubar bleibt
Wie die ID aussieht
Jede generierte ID besteht aus zwei Teilen:Präfix + numerische Folge
Beispiele:* ORD00000125* MAT00000003* TEST00004567
Das Präfix gibt an, um welche Art von Datensatz es sich handelt. Der numerische Teil ist die fortlaufende Sequenz. Ich habe mich dafür entschieden, die Konfiguration des numerischen Formats sehr einfach zu halten. Anstatt vom Benutzer eine separate Zahl wie „8 Ziffern“ zu verlangen, verwendet der Logikblock ein Nullmuster wie:* 00000000
Dies ist auf einen Blick leichter zu erkennen. Wenn jemand acht Nullen sieht, versteht er sofort, dass der Zahlenabschnitt immer acht Zeichen lang sein sollte. Deshalb habe ich idNumberFormat in dieser Form beibehalten, anstatt es durch ein reines Längenfeld zu ersetzen.
Beispiele:* 00000000 → 8-stellige Zahl* 0000 → 4-stellige Zahl
Diese Entscheidung ist zwar klein, aber sie ist wichtig. Sie macht den Logikblock zugänglicher für Menschen, die nicht in technischen Parametern denken möchten.
Wie der Build aufgebaut ist
Die eigentliche Implementierung verwendet drei Trigger. Diese Struktur ist beabsichtigt. Ich wollte, dass der Logikblock von oben nach unten leicht nachzuvollziehen ist, während die Hauptlogik dennoch separat und wiederverwendbar bleibt. Anstatt die Logik also auf viele bearbeitbare Trigger zu verteilen, habe ich sie in drei Phasen gegliedert:1. BENUTZERKONFIGURATION: EINGABEPHASE 2. MAGIC 3. BENUTZERKONFIGURATION: AUSGABEPHASE
Dies sorgt für ein gutes Gleichgewicht zwischen Benutzerfreundlichkeit und Kontrolle.* Im ersten Trigger definieren Sie die Eingaben.* Der zweite Trigger enthält die interne Logik, die normalerweise nicht bearbeitet werden muss, damit der Logikblock funktioniert.* Im dritten Trigger legen Sie fest, was mit dem generierten Ergebnis geschehen soll.
Das bedeutet, dass man normalerweise nicht im Trigger für die Kernlogik arbeiten muss. Man muss lediglich die Eingangs- und die Ausgangsseite konfigurieren. Das war eine der wichtigsten Entwurfsentscheidungen: den Logikblock wiederverwendbar zu machen, indem nur die Teile offen gelegt werden, die üblicherweise geändert werden müssen.
Trigger 1 – BENUTZERKONFIGURATION: EINGABESTUFE
Dies ist der erste Trigger, mit dem Sie interagieren. Sein Zweck besteht darin, die Eingabewerte zu definieren, die der Logikblock benötigt, bevor eine ID generiert werden kann.In der Praxis wird dieser Trigger verwendet, um Folgendes zu definieren:* das Präfix* das ID-Nummernformat* die an den Konnektor übergebene Tabellen-IDBeispiel:Präfix = ORID-Nummernformat = 00000000Tabellen-ID = Zieltabelle
Diese Stufe ist bewusst editierbar, da es sich um Werte handelt, die sich normalerweise von Anwendungsfall zu Anwendungsfall ändern.
Sie können dieselbe Schaltfläche in eine andere App oder einen anderen Schritt kopieren, diese Konfigurationswerte aktualisieren und den Logikblock wiederverwenden, ohne die interne Logik zu verändern.
Dies ermöglicht eine schnellere Implementierung, da der restliche Ablauf automatisch abläuft, sobald die Eingabewerte definiert sind. Dieser Trigger führt außerdem den Connector-Aufruf aus, der den neuesten passenden Datensatz aus der ausgewählten Tabelle ausliest.
Warum der Konnektor Teil des Designs ist
Der Konnektor ist nicht nur ein technisches Detail. Er ist Teil der Wiederverwendungsstrategie. Ich verwende den Konnektor, um die aktuellste vorhandene ID aus der Zieltabelle auszulesen. Der Konnektor erhält die Tabellen-ID als Eingabe, wendet den Präfixfilter an, sortiert die Datensätze nach _sequenceNumber in absteigender Reihenfolge und gibt den neuesten passenden Datensatz zurück. Dies ist aus zwei Gründen wichtig: Erstens ermöglicht es dem Logikblock, die Nummerierung ausgehend vom tatsächlichen Zustand der Tabelle fortzusetzen. Zweitens lässt sich der Logikblock dadurch viel einfacher wiederverwenden. Wenn Sie die Schaltfläche in eine andere App kopieren, müssen Sie die Logik nicht neu schreiben. Sie müssen lediglich den Konnektor auf die richtige Tabelle verweisen und die erforderliche Konfiguration vornehmen. Dies ist das praktische Muster hinter dem Aufbau: 1. Erstellen Sie die Schaltfläche einmal. 2. Legen Sie die Tabelleneingabe im Konnektor fest. 3. Definieren Sie das Präfix und das Zahlenformat. 4. Verwenden Sie sie bei Bedarf wieder.
Dies war eine bewusste Entscheidung, um eine schnellere App-Erstellung zu ermöglichen.
Was macht der Konnektor im Logikblock?
Was hier geschieht:* Der Konnektor empfängt die Tabellen-ID.* Der Konnektor empfängt das Präfix.* Der Konnektor filtert Datensätze nach dem Präfix.* Der Konnektor sortiert Datensätze nach _sequenceNumber in absteigender Reihenfolge.* Der Konnektor gibt die aktuellste übereinstimmende ID zurück
Der Konnektor unterstützt außerdem einen sehr wichtigen Anwendungsfall: mehrere Entitäten in derselben Tabelle.
Wenn dieselbe Tabelle Datensätze wie die folgenden enthält: ORD00000011 MAT00000003, dann sollte der Logikblock diese nicht als eine gemeinsame Sequenz behandeln. Jedes Präfix sollte erst ab seinem eigenen neuesten Wert fortgesetzt werden.
Daher filtert der Konnektor zunächst nach Präfix. Das bedeutet, dass die nächsten Werte wie folgt lauten: ORD → ORD00000012, MAT → MAT00000004
Ich habe mich für dieses Modell entschieden, da verschiedene Entitäten in derselben Tabelle vorkommen können, was jedoch nicht bedeutet, dass sie dieselbe fortlaufende Nummer teilen sollten.
Eine Anforderung an die Einrichtung sollte ebenfalls klar erwähnt werden: Der Konnektor benötigt eine Authentifizierung, daher muss ein Token generiert und den Authentifizierungseinstellungen des Konnektors hinzugefügt werden. Ich betrachte dies als Einrichtungsaufwand und nicht als Teil der Logik des Logikblocks selbst, aber es ist dennoch ein wichtiger Bestandteil, um den Logikblock nutzbar zu machen.
Ich habe dies in der Eingabestufe belassen, da es zur Einrichtung und zur Kontexterfassung gehört. Bevor die interne Logik funktionieren kann, muss der Logikblock zunächst wissen, womit er arbeitet.
Trigger 2 – MAGIC
Dies ist der Trigger der Kernlogik. Ich habe ihn bewusst so benannt. Die Idee ist einfach: Dies ist der Teil, den Sie normalerweise nicht bearbeiten müssen. Der Logikblock sollte diese Logik bereits mitbringen. Dieser Trigger nimmt die konfigurierten Eingabewerte und die zuletzt vom vorherigen Trigger zurückgegebene ID entgegen und führt dann die interne Entscheidungsfindung durch. Auch wenn es sich um einen einzigen Trigger handelt, finden in ihm mehrere Vorgänge statt.
1. Formatprüfung – der „if“-Teil des Triggers
Die erste Prüfung stellt sicher, dass die zuletzt von der Tabelle zurückgegebene ID noch der erwarteten Struktur entspricht. Erwartete Struktur: **Präfix + Zahl mit fester Länge
Beispiel für einen gültigen Wert: ORD00000025 Beispiele für ungültige Werte: ORD25, ORD-00025, ABC00000025, ORD00025A
Expression
REGEX_REPLACE( LINK(Variable._stateLastId.id, Variable._configPrefix + Variable._configIdNumberFormat), '^' + Variable._configPrefix + '[0-9]{' + LEN(Variable._configIdNumberFormat) + '}$', 'ok')
Was diese „ expression “ bewirkt
Dieser „ expression “ prüft, ob die aktuellste ID genau der vom Logikblock erwarteten Struktur entspricht. Dies geschieht in drei Schritten:* LINK(...) legt fest, welcher Wert geprüft werden soll. Wenn der Konnektor keine ID zurückgibt, wird stattdessen der zweite Teil verwendet. Dieser zweite Teil ist die konfigurierte Startstruktur und ermöglicht es dem Logikblock, die erste ID für eine neue Präfixsequenz zu generieren.* '^' + Variable._configPrefix bedeutet, dass der Wert mit dem konfigurierten Präfix beginnen muss.* '[0-9]{' + LEN(Variable._configIdNumberFormat) + '}$' bedeutet, dass der Wert mit genau der erwarteten Anzahl von Ziffern enden muss und danach keine weiteren Zeichen erlaubt sind.
Wenn der gesamte Wert der erwarteten Struktur entspricht, lautet das Ergebnis „ok“. Ist dies nicht der Fall, bleibt das Ergebnis von „ expression “ ungleich „ok“, und der Trigger stoppt den Generierungsablauf und zeigt eine Fehlermeldung an
Warum ich mich dafür entschieden habe
Ich habe diese Prüfung hinzugefügt, weil ich nicht wollte, dass der Logikblock bei einer fehlerhaften ID fortgesetzt wird. Wenn der zuletzt gespeicherte Wert falsch ist und die Logik ihn akzeptiert, kann sich das falsche Muster auf zukünftige Datensätze ausbreiten. Daher wollte ich, dass die erste Aufgabe des MAGIC-Triggers darin besteht, dies frühzeitig zu unterbinden. Wenn das Format falsch ist: * _stateOutput wird gelöscht * Es wird eine Meldung angezeigt, die erklärt, warum die Generierung gestoppt wurde
2. Überlaufprüfung
Die zweite Prüfung untersucht den nächsten numerischen Wert und stellt sicher, dass er weiterhin innerhalb der konfigurierten numerischen Länge liegt.
Beispiel: idNumberFormat = 0000
Zulässiger Bereich: 0000 bis 9999
Wenn der nächste Wert 10000 betragen würde, würde er nicht mehr in die konfigurierte Struktur passen.
Expression
LEN(Variable._configIdNumberFormat) < LEN( TOTEXT( TEXTTOINTEGER( REGEX_REPLACE( LINK(Variable._stateLastId.id, Variable._configPrefix + Variable._configIdNumberFormat), '^' + Variable._configPrefix, '' ) ) + 1 ))
Was diese „ expression “ bewirkt
Diese „ expression “ vergleicht zwei Längen:* die zulässige numerische Länge, die aus „idNumberFormat“ entnommen wird* die Länge der nächsten Zahl nach der Erhöhung des aktuellen Werts
Die rechte Seite funktioniert wie folgt:* LINK(...) verwendet die vom Connector zurückgegebene ID, sofern vorhanden. Wenn der Connector keine ID zurückgibt, wird stattdessen die konfigurierte Startstruktur verwendet. Dies ermöglicht die Generierung der ersten ID.* REGEX_REPLACE(...) entfernt das Präfix, sodass nur der numerische Teil übrig bleibt* TEXTTOINTEGER(...) + 1 berechnet den nächsten numerischen Wert* TOTEXT(...) wandelt diesen nächsten Wert wieder in Text um* LEN(...) ermittelt, wie viele Ziffern dieser nächste Wert benötigt
Wenn die nächste Zahl mehr Ziffern benötigt, als das Format zulässt, wird die Bedingung wahr und der Trigger stoppt den Vorgang.
Warum ich mich dafür entschieden habe
Ich habe mich dafür entschieden, den Prozess an dieser Stelle zu stoppen, anstatt die Struktur automatisch zu ändern. Diese Entscheidung war wichtig, da ich wollte, dass die ID-Länge stabil bleibt, sofern nicht jemand die Konfiguration explizit ändert. Wenn ein Überlauf erkannt wird:* _stateOutput wird gelöscht* Es wird eine Meldung angezeigt, die erklärt, dass die maximale Länge erreicht wurde
3. ID-Generierung
Wenn beide Prüfungen erfolgreich sind, generiert der Trigger die nächste ID. Beispiel: ORD00000015 → ORD00000016
Derselbe „ expression “ behandelt auch den Fall, dass für dieses Präfix noch kein Datensatz existiert. In diesem Fall greift die Logik auf die konfigurierte Startstruktur zurück und erzeugt den ersten Wert in der Sequenz. Beispiel: ORD00000001
Expression
Variable._configPrefix + RIGHT( Variable._configIdNumberFormat + TOTEXT( TEXTTOINTEGER( REGEX_REPLACE( LINK(Variable._stateLastId.id, Variable._configPrefix + Variable._configIdNumberFormat), '^' + Variable._configPrefix, '' ) ) + 1 ), LEN(Variable._configIdNumberFormat))
Was diese „ expression “ bewirkt
Dies ist der „ expression “, der die endgültige ID bildet. Er arbeitet in folgender Reihenfolge:* LINK(...) verwendet die zuletzt vom Connector zurückgegebene ID, sofern eine vorhanden ist. Wenn der Connector keine ID zurückgibt, wird stattdessen die konfigurierte Startstruktur verwendet. Dadurch kann derselbe „ expression “ auch die erste generierte ID verarbeiten.* REGEX_REPLACE(...) entfernt das Präfix, damit der numerische Teil verarbeitet werden kann* TEXTTOINTEGER(...)+ 1 erhöht den Wert um 1* TOTEXT(...) wandelt die neue Zahl wieder in Text um* Variable._configIdNumberFormat + ... fügt das Nullmuster vor dem neuen Wert hinzu* RIGHT(..., LEN(Variable._configIdNumberFormat)) behält nur die erforderliche Anzahl an Ziffern bei* Variable._configPrefix + ... fügt das Präfix wieder hinzu, um die endgültige ID zu erstellen. Das Ergebnis wird in _stateOutput gespeichert.
Warum ich mich dafür entschieden habe
Ich habe dies bewusst als einen zentralen „ expression “ beibehalten. Ich wollte keine separaten internen Verzweigungen für die erste und die nächste ID, sofern ich dies vermeiden konnte. Die Berechnung an einer Stelle zu belassen, erleichtert die Wiederverwendung und macht zukünftige Änderungen sicherer. Am Ende dieses Triggers wird der generierte Wert in _stateOutput gespeichert.
Trigger 3 – BENUTZERKONFIGURATION: AUSGABESTUFE
Dies ist der letzte Trigger im Logikblock. Sein Zweck besteht darin, festzulegen, was mit der generierten ID geschehen soll, sobald _stateOutput einen gültigen Wert enthält. Wichtig ist hierbei, dass diese Stufe bewusst offen für Sie gelassen wurde. Ist _stateOutput nicht leer, können Sie entscheiden, was der Logikblock als Nächstes tun soll.In der aktuellen Konfiguration zeigt der Trigger eine Hilfemeldung an, die erklärt, was als Nächstes zu tun ist:* Füge die konfigurierte Tabelle zur App hinzu* Ersetze diesen Meldungsschritt durch einen „Create Record “-Trigger* Verwende _stateOutput als ID für den neuen Datensatz
Ich habe es so aufgebaut, da das Ausgabeverhalten je nach Anwendungsfall variieren kann. Möglicherweise möchten Sie sofort einen Datensatz anlegen. Vielleicht möchten Sie den Wert zunächst in einer anderen Variablen speichern. Oder Sie möchten ihn vielleicht in einem größeren Workflow verwenden, bevor Sie etwas in die Tabelle schreiben. Anstatt also ein festes Ausgabeverhalten festzulegen, habe ich die Ausgabestufe konfigurierbar gehalten. Deshalb gibt es den benutzerseitigen Teil an beiden Enden:* Die Eingabe kann konfiguriert werden* Die Ausgabe kann konfiguriert werden* Die Kernlogik bleibt in der Mitte
Warum die Trigger so getrennt sind
Diese Struktur spiegelt eine der wichtigsten Entwurfsentscheidungen im gesamten Logikblock wider: Verantwortlichkeiten trennen, die Kernlogik jedoch zentralisieren.Deshalb habe ich nicht alles in einen einzigen riesigen Trigger gepackt. Ein einziger riesiger Trigger mag zwar funktionieren, wird aber schwerer lesbar, schwerer zu erklären und schwerer zu debuggen. Andererseits wird es auch unübersichtlich, wenn man die eigentliche Mathematik in viele kleine Teile aufteilt. Deshalb habe ich einen Mittelweg gewählt:* separate Trigger für übersichtliche Schritte* ein zentraler „ expression “ für die eigentliche ID-Berechnung
Dadurch profitiere ich von den Vorteilen beider Ansätze:* Der Aufbau ist leichter nachzuvollziehen* Die Logik lässt sich leichter warten* Die Wiederverwendung ist einfacher* Zukünftige Änderungen sind weniger riskant
Warum Validierung und Schutz in diesem Logikblock wichtig sind
Bei diesem Logikblock geht es nicht nur darum, die nächste Zahl zu generieren. Es geht auch darum, die Tabelle vor fehlerhaften Daten zu schützen. Es gibt zwei wesentliche Schutzaspekte:
Formatschutz
Der Logikblock prüft, ob die zuletzt vorhandene ID der erwarteten Struktur entspricht, bevor er fortfährt. Dies verhindert, dass die Sequenz auf der Grundlage von IDs fortgesetzt wird, die nicht der erwarteten Struktur entsprechen.
Überlaufschutz
Der Logikblock prüft, ob die nächste Zahl noch innerhalb der zulässigen numerischen Länge liegt. Dadurch wird verhindert, dass sich die Struktur unerwartet ändert, wenn die Sequenz wächst. Ich habe beide Aspekte als wichtig erachtet, da fehlerhafte ID-Daten sich schnell ausbreiten können. Sobald ein falsches Muster in eine Tabelle gelangt und der Generator beginnt, es zu verwenden, kann dieses Problem jeden zukünftigen Datensatz beeinträchtigen. Daher ist der Logikblock so konzipiert, dass er frühzeitig stoppt, anstatt stillschweigend in die falsche Richtung fortzufahren.
Vorgehensweise bei der Variablennamensgebung
Möglicherweise fällt Ihnen auf, dass die innerhalb des Logikblocks verwendeten Variablennamen einem bestimmten Muster folgen. Beispiele:_configPrefix_configIdNumberFormat_stateLastId_sequenceNumber
Ich habe bewusst einen Namensstil gewählt, der sich leicht von Variablen unterscheiden lässt, die typischerweise innerhalb von Apps erstellt werden. Der Zweck ist nicht technischer, sondern praktischer Natur. Wenn Sie den Logikblock in eine App importieren oder kopieren, sollte sofort erkennbar sein, welche Variablen zum Logikblock gehören und welche zur App-Logik.Variablen des Logikblocks weisen eine einheitliche Struktur auf, sodass sie optisch als Gruppe erkennbar sind und es weniger wahrscheinlich ist, dass es zu Konflikten mit benutzerdefinierten Variablen kommt. Dadurch lässt sich der Logikblock leichter wiederverwenden und sicherer in bestehende Apps integrieren, ohne dass es zu Namenskonflikten oder Verwechslungen kommt.
Zusammenfassung der Entwurfsentscheidungen
Wenn ich den gesamten Logikblock auf die ihm zugrunde liegenden Kernentscheidungen reduziere, sind dies die folgenden:* Ich wollte eine schnelle Wiederverwendung, daher ist der Logikblock konfigurationsgesteuert.* Ich wollte, dass die Tabelle die „Quelle der Wahrheit“ ist, daher basiert die nächste ID auf dem neuesten tatsächlichen Datensatz.* Ich wollte, dass die Logik wartbar bleibt, daher ist die Berechnung zentralisiert.* Ich wollte, dass mehrere Entitäten in einer Tabelle reibungslos funktionieren, daher hat jedes Präfix seine eigene Sequenz.* Ich wollte die Tabelle vor fehlerhaften Mustern schützen, daher verhindern Format- und Überlaufprüfungen fehlerhafte Daten bereits vor der Datensatzerstellung.* Ich wollte, dass der Trigger-Ablauf lesbar ist, daher sind die Aufgaben in klare Schritte unterteilt.
Deshalb sieht der Logikblock so aus, wie er aussieht. Er ist nicht nur ein ID-Generator. Es handelt sich um ein wiederverwendbares Muster für die kontrollierte ID-Erstellung, das versucht, Einfachheit, Wiederverwendbarkeit und Datenschutz in Einklang zu bringen.
Machen Sie mit
Treten Sie der Community bei, um Verbesserungen zu teilen, zusätzliche Einheiten vorzuschlagen, Probleme zu melden oder architektonische Entscheidungen zu diskutieren.
