Générateur d’identifiants séquentiels

Prev Next

To download the Sequential ID Generator, visit: [tulip](https://<span translate=).co/library/apps/sequential-id-generator---multiple-prefix-support">Library

Dans cet article, Tamás, l’un de nos développeurs de la bibliothèque, revient sur les choix techniques qui ont présidé au développement de l’application « Sequential ID Generator » disponible dans la bibliothèque.

Pourquoi je l’ai conçue ainsi

Je souhaitais que ce bloc logique résolve un problème très concret : générer des identifiants de manière fiable, facile à réutiliser et rapide à déployer dans de nombreuses applications.

L’objectif principal de conception ne se limitait pas à « générer le numéro suivant ». Le véritable objectif était de créer quelque chose qui, une fois défini, puisse être copié et réutilisé avec un minimum d’effort. Si un bouton est créé une seule fois de manière claire, il devrait être possible de l’intégrer dans une autre application ou une autre étape et de ne modifier que la configuration. Je ne voulais pas que chaque développeur d’application ait à rouvrir la logique et à réécrire les déclencheurs à chaque fois.

C’est pourquoi le bloc logique est conçu pour privilégier la configuration plutôt que la logique. Lorsque vous utilisez ce bloc logique, vous devez principalement réfléchir aux points suivants :* quel préfixe est nécessaire* quelle doit être la longueur de la partie numérique* dans quelle table le bouton doit-il lire les données

Une fois que vous avez défini le préfixe, le format du nombre et la table, les blocs logiques devraient déjà se charger du reste : lire le dernier ID, le valider, générer la valeur suivante et vérifier si le nombre respecte toujours la longueur configurée.

La table elle-même doit être la source de vérité. Un compteur séparé (qui peut être une variable, par exemple) peut s’écarter des données réelles si des enregistrements sont insérés, supprimés ou modifiés de manière imprévue. La lecture à partir de la table permet de maintenir la numérotation en lien avec ce qui existe réellement.

Une autre décision importante a été de regrouper la logique de calcul principale en un seul endroit. Je ne voulais pas d’une branche de déclencheur pour le premier ID, d’une autre pour l’ID suivant, puis de petits fragments de déclencheurs supplémentaires qui deviendraient par la suite difficiles à suivre. Je souhaitais que la logique principale soit regroupée dans un seul « expression » afin que le comportement reste cohérent et que toute personne amenée à la modifier par la suite sache exactement où chercher.

Le bloc logique repose donc en réalité sur quelques choix pratiques :* facile à copier et à réutiliser* suffisamment sûr pour protéger la table contre les mauvaises pratiques de numérotation* simple à configurer* logique centralisée pour que la maintenance reste gérable

À quoi ressemble l’identifiant ?

Chaque identifiant généré comporte deux parties :un préfixe + une séquence numérique

Exemples :* ORD00000125* MAT00000003* TEST00004567

Le préfixe indique de quel type d’enregistrement il s’agit. La partie numérique correspond à la séquence courante. J’ai choisi de garder la configuration du format numérique très simple. Au lieu de demander à l’utilisateur un nombre spécifique tel que « 8 chiffres », le bloc logique utilise un modèle de zéros tel que :* 00000000

C'est plus facile à lire d'un seul coup d'œil. Si quelqu'un voit huit zéros, il comprend immédiatement que la partie numérique doit toujours comporter huit caractères. C'est pourquoi j'ai conservé idNumberFormat sous cette forme au lieu de le remplacer par un champ de longueur pure.

Exemples :* 00000000 → nombre à 8 chiffres* 0000 → nombre à 4 chiffres

Cette décision est mineure, mais elle a son importance. Elle rend le bloc logique plus accessible aux personnes qui ne souhaitent pas raisonner en termes de paramètres techniques.

Comment la structure est organisée

La mise en œuvre proprement dite utilise trois déclencheurs. Cette structure est intentionnelle. Je souhaitais que le bloc logique soit facile à suivre de haut en bas, tout en conservant la logique principale distincte et réutilisable. Ainsi, au lieu de répartir la logique sur de nombreux déclencheurs modifiables, je l’ai organisée en trois étapes :1. CONFIGURATION UTILISATEUR : ÉTAPE D’ENTRÉE 2. MAGIE 3. CONFIGURATION UTILISATEUR : ÉTAPE DE SORTIE

Cela offre un bon équilibre entre facilité d’utilisation et contrôle.* Le premier déclencheur est celui où vous définissez les entrées.* Le deuxième déclencheur contient la logique interne qui, en règle générale, n’a pas besoin d’être modifiée pour que le bloc logique fonctionne.* Le troisième déclencheur est celui où vous décidez quoi faire du résultat généré.

Cela signifie que vous n’avez généralement pas besoin d’intervenir dans le déclencheur de la logique principale. Il suffit de configurer les côtés entrée et sortie. C’était l’une des décisions clés de conception : rendre le bloc logique réutilisable en n’exposant que les parties qui doivent habituellement être modifiées.

Déclencheur 1 – CONFIGURATION UTILISATEUR : ÉTAPE D’ENTRÉE

Il s’agit du premier déclencheur avec lequel vous interagissez. Son objectif est de définir les valeurs d’entrée dont le bloc logique a besoin avant qu’un identifiant puisse être généré.En pratique, ce déclencheur sert à définir :* le préfixe* le format d’identifiant (idNumberFormat)* l’ID de table transmis au connecteurExemple :préfixe = ORDidNumberFormat = 00000000ID de table = table cible

Cet étage est volontairement modifiable, car ce sont ces valeurs qui varient généralement d’un cas d’utilisation à l’autre.

Vous pouvez reprendre ce même bouton, le copier dans une autre application ou une autre étape, mettre à jour ces valeurs de configuration et réutiliser le bloc logique sans modifier la logique interne.

Cela permet une mise en œuvre plus rapide car, une fois les valeurs d’entrée définies, le reste du flux fonctionne automatiquement. Ce déclencheur exécute également l’appel du connecteur qui lit le dernier enregistrement correspondant dans la table sélectionnée.

Pourquoi le connecteur fait-il partie de la conception ?

Le connecteur n’est pas seulement un détail technique. Il fait partie intégrante de la stratégie de réutilisation. J’utilise le connecteur pour lire le dernier identifiant existant dans la table cible. Le connecteur reçoit l’ID de la table en entrée, applique le filtre de préfixe, trie les enregistrements par _sequenceNumber dans l’ordre décroissant, puis renvoie le dernier enregistrement correspondant. Ceci est important pour deux raisons. Premièrement, cela permet au bloc logique de poursuivre la numérotation à partir de l’état réel de la table. Deuxièmement, cela facilite considérablement la réutilisation du bloc logique. Si vous copiez le bouton dans une autre application, vous n’avez pas besoin de réécrire la logique. Il vous suffit de pointer le connecteur vers la bonne table et de fournir la configuration nécessaire. Voici le modèle pratique qui sous-tend cette construction : 1. créer le bouton une seule fois ; 2. définir la table en entrée dans le connecteur ; 3. définir le préfixe et le format du numéro ; 4. le réutiliser partout où cela est nécessaire.

Il s’agit d’un choix délibéré visant à accélérer la création d’applications.

Que fait le connecteur dans le bloc logique ?

Voici ce qui se passe : * le connecteur reçoit l’ID de la table * le connecteur reçoit le préfixe * le connecteur filtre les enregistrements par préfixe * le connecteur trie les enregistrements par _sequenceNumber par ordre décroissant * le connecteur renvoie le dernier ID correspondant

Le connecteur prend également en charge un cas d’utilisation très important : la présence de plusieurs entités dans une même table.

Si une même table contient des enregistrements tels que :ORD00000011MAT00000003, le bloc logique ne doit pas les traiter comme une seule séquence partagée. Chaque préfixe doit reprendre uniquement à partir de sa propre dernière valeur.

Le connecteur effectue donc d’abord un filtrage par préfixe. Cela signifie que les valeurs suivantes deviennent : ORD → ORD00000012, MAT → MAT00000004

J’ai choisi ce modèle car différentes entités peuvent coexister dans la même table, mais cela ne signifie pas pour autant qu’elles doivent partager le même numéro séquentiel.

Une exigence de configuration mérite également d’être clairement précisée : le connecteur nécessite une authentification ; un jeton doit donc être généré et ajouté aux paramètres d’authentification du connecteur. Je considère cela comme une tâche de configuration, et non comme faisant partie de la logique du bloc logique lui-même, mais cela reste un élément important pour rendre le bloc logique utilisable.

J’ai conservé cette étape dans la phase d’entrée, car elle relève de la configuration et de la collecte du contexte. Avant que la logique interne puisse fonctionner, le bloc logique doit d’abord savoir sur quoi il va travailler.

Déclencheur 2 – MAGIC

Il s’agit du déclencheur de la logique principale. Je l’ai nommé ainsi à dessein. L’idée est simple : c’est la partie que vous ne devriez normalement pas avoir à modifier. Le bloc logique devrait déjà intégrer cette logique. Ce déclencheur prend les valeurs d’entrée configurées et le dernier identifiant renvoyé par le déclencheur précédent, puis effectue la prise de décision interne. Même s’il s’agit d’un seul déclencheur, plusieurs opérations s’y déroulent.

1. Validation du format – la partie « if » du déclencheur

La première vérification s'assure que le dernier ID renvoyé par la table correspond toujours à la structure attendue. Structure attendue : **préfixe + nombre de longueur fixe

Exemple de valeur valide : ORD00000025 Exemples de valeurs non valides : ORD25, ORD-00025, ABC00000025, ORD00025A

Expression

REGEX_REPLACE( LINK(Variable._stateLastId.id, Variable._configPrefix + Variable._configIdNumberFormat), '^' + Variable._configPrefix + '[0-9]{' + LEN(Variable._configIdNumberFormat) + '}$', 'ok')

Fonctionnalité de cette expression

Cette « expression » vérifie si le dernier ID respecte exactement la structure attendue par le bloc logique. Elle procède en trois étapes : * LINK(...) détermine quelle valeur doit être vérifiée. Si le connecteur ne renvoie pas d’ID, la deuxième partie est utilisée à la place. Cette deuxième partie correspond à la structure de début configurée et permet au bloc logique de générer le premier ID d’une nouvelle séquence de préfixes.* « ^ » + Variable._configPrefix signifie que la valeur doit commencer par le préfixe configuré.* « [0-9]{ » + LEN(Variable._configIdNumberFormat) + « }$ » signifie que la valeur doit se terminer par exactement le nombre de chiffres attendu, et qu’aucun caractère supplémentaire n’est autorisé après cela.

Si la valeur entière correspond à la structure attendue, le résultat est « ok ». En cas de non-correspondance, le résultat de l’ expression e reste différent de « ok », et le déclencheur interrompt le flux de génération et affiche un message d’erreur

Pourquoi j’ai fait ce choix

J’ai ajouté cette vérification car je ne voulais pas que le bloc logique se poursuive à partir d’un identifiant mal formé. Si la dernière valeur enregistrée est erronée et que la logique l’accepte, ce modèle incorrect risque alors de se propager dans les futurs enregistrements. J’ai donc voulu que la première responsabilité du déclencheur MAGIC soit d’interrompre ce processus dès le début. Si le format est incorrect : * _stateOutput est effacé * un message s’affiche pour expliquer pourquoi la génération s’est arrêtée

2. Validation du débordement

Le deuxième contrôle examine la valeur numérique suivante et vérifie qu’elle respecte toujours la longueur numérique configurée.

Exemple : idNumberFormat = 0000

Plage autorisée : de 0000 à 9999

Si la valeur suivante devenait : 10000, elle ne correspondrait plus à la structure configurée.

Expression

LEN(Variable._configIdNumberFormat) < LEN( TOTEXT( TEXTTOINTEGER( REGEX_REPLACE( LINK(Variable._stateLastId.id, Variable._configPrefix + Variable._configIdNumberFormat), '^' + Variable._configPrefix, '' ) ) + 1 ))

Fonctionnement de cette expression

Cette expression compare deux longueurs :* la longueur numérique autorisée, issue de idNumberFormat* la longueur du nombre suivant après incrémentation de la valeur actuelle

Le côté droit fonctionne ainsi :* LINK(...) utilise l’ID renvoyé par le connecteur lorsqu’il en existe un. Si le connecteur ne renvoie pas d’ID, il utilise à la place la structure de départ configurée. C’est ce qui permet la génération du premier ID.* REGEX_REPLACE(...) supprime le préfixe afin de ne conserver que la partie numérique* TEXTTOINTEGER(...) + 1 calcule la valeur numérique suivante* TOTEXT(...) reconvertit cette valeur suivante en texte* LEN(...) détermine le nombre de chiffres nécessaires pour cette valeur suivante

Si le nombre suivant nécessite plus de chiffres que ne le permet le format, la condition devient vraie et le déclencheur interrompt le processus.

Pourquoi j’ai fait ce choix

J’ai choisi d’arrêter le processus à ce stade plutôt que de modifier automatiquement la structure. Cette décision était importante car je souhaitais que la longueur des identifiants reste stable, à moins que quelqu’un ne modifie explicitement la configuration. Si un débordement est détecté :* _stateOutput est effacé* un message s’affiche pour expliquer que la longueur maximale a été atteinte

3. Génération d’ID

Si les deux vérifications sont réussies, le déclencheur génère l’ID suivant. Exemple : ORD00000015 → ORD00000016

Ce même « expression » gère également le cas où aucun enregistrement n’existe encore pour ce préfixe. Dans ce cas, la logique revient à la structure de départ configurée et produit la première valeur de la séquence. Exemple : 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))

Fonctionnement de cette expression

Il s’agit de l’ expression qui génère l’ID final. Elle fonctionne dans l’ordre suivant :* LINK(...) utilise le dernier ID renvoyé par le connecteur lorsqu’il en existe un. Si le connecteur ne renvoie pas d’ID, elle utilise à la place la structure de départ configurée. C’est ce qui permet à la même expression de gérer également le premier ID généré.* REGEX_REPLACE(...) supprime le préfixe afin que la partie numérique puisse être traitée* TEXTTOINTEGER(...)+ 1 incrémente le nombre* TOTEXT(...) reconvertit le nouveau nombre en texte* Variable._configIdNumberFormat + ... ajoute le zéro de remplissage devant la nouvelle valeur* RIGHT(..., LEN(Variable._configIdNumberFormat)) ne conserve que le nombre de chiffres requis* Variable._configPrefix + ... rajoute le préfixe pour créer l’ID final. Le résultat est stocké dans _stateOutput.

Pourquoi j’ai choisi cette approche

J’ai délibérément conservé cette approche sous la forme d’un « expression » central. Je ne souhaitais pas créer de branches internes distinctes pour le premier identifiant et l’identifiant suivant si je pouvais l’éviter. Le fait de regrouper le calcul en un seul endroit facilite la réutilisation et rend les modifications futures plus sûres. À la fin de ce déclencheur, la valeur générée est stockée dans _stateOutput.

Déclencheur 3 – CONFIGURATION UTILISATEUR : ÉTAPE DE SORTIE

Il s’agit du dernier déclencheur du bloc logique. Son objectif est de définir ce qu’il faut faire avec l’ID généré une fois que _stateOutput contient une valeur valide. Le point important ici est que cette étape vous est intentionnellement laissée libre. Si _stateOutput n’est pas vide, vous pouvez alors décider de la suite des opérations du bloc logique.Dans la configuration actuelle, le déclencheur affiche un message d’aide expliquant la marche à suivre :* ajouter la table configurée à l’application* remplacer cette étape de message par un déclencheur « Créer un enregistrement »* utiliser _stateOutput comme identifiant du nouvel enregistrement

J’ai conçu ce déclencheur de cette manière car le comportement de sortie peut varier selon les cas d’utilisation. Vous souhaiterez peut-être créer un enregistrement immédiatement. Vous souhaiterez peut-être d’abord stocker la valeur dans une autre variable. Ou encore, vous souhaiterez peut-être l’utiliser dans un workflow plus vaste avant d’écrire quoi que ce soit dans la table. Ainsi, plutôt que de définir un comportement de sortie fixe, j’ai veillé à ce que l’étape de sortie reste configurable. C’est pourquoi la partie visible par l’utilisateur se trouve aux deux extrémités :* l’entrée peut être configurée* la sortie peut être configurée* la logique centrale reste au milieu

Pourquoi les déclencheurs sont-ils séparés de cette manière ?

Cette structure reflète l’un des principaux choix de conception de l’ensemble du bloc logique : séparer les responsabilités, tout en centralisant la logique principale.C’est pourquoi je n’ai pas tout regroupé dans un seul énorme déclencheur. Un seul déclencheur géant pourrait fonctionner, mais il deviendrait plus difficile à lire, à expliquer et à déboguer. D’un autre côté, diviser les calculs mathématiques en de nombreux petits morceaux rend également le code confus. J’ai donc choisi une voie intermédiaire :* des déclencheurs distincts pour des étapes lisibles* une « expression » centralisée pour le calcul réel de l’ID

Cela me permet de bénéficier des avantages des deux approches :* la structure est plus facile à suivre* la logique est plus facile à maintenir* la réutilisation est plus simple* les modifications futures présentent moins de risques

Pourquoi la validation et la protection sont importantes dans ce bloc logique

Ce bloc logique ne sert pas uniquement à générer le nombre suivant. Il sert également à protéger la table contre les données erronées. Il existe deux points de protection principaux :

Protection du format

Le bloc logique vérifie si le dernier identifiant existant correspond à la structure attendue avant de poursuivre. Cela empêche la séquence de se poursuivre sur la base d’identifiants qui ne correspondent pas à la structure attendue.

Protection contre les débordements

Le bloc logique vérifie si le numéro suivant respecte toujours la longueur numérique autorisée. Cela empêche la structure de changer de manière inattendue à mesure que la séquence s'allonge. J'ai jugé ces deux aspects importants, car des données d'identifiant erronées peuvent se propager rapidement. Dès qu’un modèle incorrect pénètre dans une table et que le générateur commence à l’utiliser, ce problème peut affecter tous les enregistrements futurs. Le bloc logique est donc conçu pour s’arrêter rapidement plutôt que de continuer en silence dans la mauvaise direction.

Approche de nommage des variables

Vous remarquerez peut-être que les noms de variables utilisés au sein du bloc logique suivent un modèle spécifique. Exemples :_configPrefix_configIdNumberFormat_stateLastId_sequenceNumber

J’ai délibérément opté pour un style de nommage facile à distinguer des variables généralement créées au sein des applications. L’objectif n’est pas technique, mais pratique. Lorsque vous importez ou copiez le bloc logique dans une application, il doit être immédiatement évident quelles variables appartiennent au bloc logique et lesquelles relèvent de la logique de l’application.Les variables du bloc logique utilisent une structure cohérente, ce qui permet de les regrouper visuellement et réduit le risque de conflit avec les variables définies par l’utilisateur. Cela facilite la réutilisation du bloc logique et rend son intégration dans des applications existantes plus sûre, sans conflit de nom ni confusion.

Résumé des choix de conception

Si je résume l’ensemble du bloc logique à ses décisions fondamentales, celles-ci sont les suivantes :* Je souhaitais une réutilisation rapide, c’est pourquoi le bloc logique est piloté par la configuration.* Je souhaitais que la table soit la source de vérité, c’est pourquoi l’ID suivant est basé sur le dernier enregistrement réel.* Je souhaitais que la logique reste facile à maintenir, c’est pourquoi le calcul est centralisé.* Je souhaitais que plusieurs entités dans une même table fonctionnent correctement, c’est pourquoi chaque préfixe dispose de sa propre séquence.* Je souhaitais protéger la table contre les mauvaises pratiques, c’est pourquoi les vérifications de format et de débordement bloquent les données erronées avant la création de l’enregistrement.* Je souhaitais que le flux du déclencheur soit lisible, c’est pourquoi les responsabilités sont réparties en étapes claires.

C’est pourquoi le bloc logique se présente ainsi. Il ne s’agit pas seulement d’un générateur d’identifiants. C’est un modèle réutilisable pour la création contrôlée d’identifiants qui cherche à trouver un équilibre entre simplicité, réutilisation et protection des données.

Participez

Rejoignez la communauté pour partager des améliorations, proposer des unités supplémentaires, signaler des problèmes ou discuter des choix architecturaux.