Construisez votre première application
À la fin de ce tutoriel, vous aurez un petit CRM avec trois tables (company,
contact, deal), une page liste-et-détail pour chacune, un Business
Event qui estampille automatiquement le updated_at du deal et un Script Module
qui recalcule la valeur totale des deals ouverts de la company chaque fois qu'un deal
change. Chaque concept des Core concepts apparaît au moins une
fois.
Il suppose que vous avez fini Démarrage et que vous avez un environnement Archestack ouvert dans un autre onglet. Durée totale : environ 45 minutes si vous lisez attentivement, 25 si vous survolez.
Conventions utilisées ici : les noms de tables et de colonnes sont en
snake_case (la plateforme suggère cette convention dans le champ "Table name" du Schema
Designer). Les exemples de code utilisent la vraie API Db, voir
Reference -> APIs appelables depuis un script
pour la surface complète.
Étape 1 - Concevez le schéma (10 min)
Ouvrez Schema Designer et ajoutez trois tables.
Pour chaque table : cliquez sur Add Table dans la barre d'outils, tapez le
nom dans le champ "Table name" de la boîte de dialogue, cliquez sur Create.
La nouvelle table apparaît sur le canevas avec une clé primaire id SERIAL
auto-générée (verrouillée). Le panneau de droite s'ouvre sur l'onglet Columns, ajoutez les
colonnes supplémentaires depuis la boîte "Add column" à bordure pointillée en bas. Cliquez
sur chaque colonne pour la développer et ajuster les commutateurs (PK / NULL / UQ), la
Length et la valeur Default.
Table : company
id· SERIAL, PK (auto)name· VARCHAR(200), requisindustry· VARCHAR(80), nullableopen_deal_value· DECIMAL, défaut0- tenu à jour via le script de l'étape 5created_at· TIMESTAMPTZ, défautnow()
Table : contact
id· SERIAL, PK (auto)first_name,last_name· VARCHAR(100), requisemail· VARCHAR(200), requis, UQ activécompany_id· INTEGER, requis - clé étrangère, configurée ensuite
Table : deal
id· SERIAL, PK (auto)company_id· INTEGER, requis - clé étrangère, configurée ensuitetitle· VARCHAR(200), requisstage· VARCHAR(40), requis, défaut'New'- valeurs : New, Qualified, Proposal, Won, Lostamount· DECIMAL, requis, défaut0created_at,updated_at· TIMESTAMPTZ, défautnow()
Clés étrangères
Les clés étrangères résident dans l'onglet Relations du panneau de droite.
Sélectionnez la table contact, basculez sur Relations, faites défiler jusqu'à la
section "Add relationship" :
- FK column (on this table) =
company_id - Relation type =
One-to-Many - References -> table =
company - References -> column =
id - Cliquez sur Add Relationship.
Répétez pour la table deal (son company_id -> company.id).
company / contact / deal aura la même forme avec deux lignes FK pointant vers company.)Déployez
Cliquez sur Deploy dans la barre d'outils du Schema Designer. Vous arrivez
sur la page de configuration du déploiement. Basculez sur l'onglet Generated SQL,
vous devriez voir trois instructions CREATE TABLE plus les contraintes FK.
Cliquez sur Deploy en haut à droite.
Ce que vous verrez si tout marche : la ligne de déploiement dans Database Deployments -> Overview passe de "Executing" à "Succeeded" en quelques secondes. Les trois tables apparaissent dans la liste des tables de l'Object Browser.
Si le déploiement échoue sur les contraintes FK : le SQL généré émet les tables dans l'ordre où elles ont été enregistrées. Ouvrez l'onglet SQL et réorganisez pour quecompanysoit créée avantcontactetdeal, puis redéployez. Ou cliquez sur Regenerate après avoir manuellement réorganisé les tables dans le canevas du Schema Designer.
Étape 2 - Composez les Business Entities (10 min)
Chaque page a besoin d'un Business Entity auquel se lier. Ouvrez Business Entities et cliquez sur Create pour chacun. Cliquez sur Run Preview après chaque sauvegarde pour confirmer que les colonnes reviennent correctement.
BE : company
Entity Name company, Master Table company, Label Column
name. Enregistrez.
Cliquez sur Add Join deux fois pour ajouter deux jointures agrégées :
-
Jointure vers
contact: From columncompany.id, To columncontact.company_id. Basculez Aggregate Mode. Choisissez la colonneid, réglez Aggregate Function =COUNT, nommez-lacontact_count. -
Jointure vers
deal: From columncompany.id, To columndeal.company_id. Aggregate Mode activé. Choisissez la colonneid, fonctionCOUNT, nommez-laopen_deal_count. (Nous filtrerons sur les deals "ouverts" via un Business Event à l'étape 5 ; pour l'instant cela compte tous les deals.)
BE : contact
Entity Name contact, Master Table contact, Label Column
email. Ajoutez une jointure vers company via
contact.company_id -> company.id, exposez la colonne name sous
company_name. (N'activez pas Aggregate Mode ici, c'est une jointure normale.)
BE : deal
Entity Name deal, Master Table deal, Label Column
title. Ajoutez une jointure vers company via
deal.company_id -> company.id, exposez la colonne name sous
company_name.
Vérifiez : sur chaque BE, cliquez sur Run Preview. Vous verrez une grille vide (pas encore de données) avec les en-têtes de colonne que vous avez définis. Si un en-tête de colonne jointe manque, vous avez probablement oublié de cocher la colonne dans le sélecteur de colonnes de la jointure. Rouvrez la jointure, cochez la colonne, enregistrez, relancez l'aperçu.
Étape 3 - Construisez les pages (10 min)
Ouvrez Page Editor. Pour chaque Business Entity, cliquez sur Create :
- Companies - Page Name
Companies, Page Route/companies, Business Entitycompany. L'onglet Visual génère automatiquement une liste et un formulaire de détail. Dans le formulaire de détail, cliquez sur Add Tab deux fois pour ajouter les onglets Contacts et Deals. Dans chaque onglet, ajoutez une section RelatedGrid liée au BE contact / deal avec le filtre de jointure réglé sur company.id = id de l'enregistrement courant. - Contacts - Page Name
Contacts, Page Route/contacts, Business Entitycontact. Le champcompany_iddu formulaire de détail auto-généré apparaîtra comme un nombre, changez son Type en Select et réglez l'autocomplete Entity surcompanypour que les utilisateurs choisissent une company par nom. - Deals - Page Name
Deals, Page Route/deals, Business Entitydeal. Même traitement pourcompany_id(TypeSelect, Entitycompany). Pourstage, laissez le Type surTextpour l'instant, les valeurs peuvent être imposées via un eventValidateplus tard si vous le souhaitez.
Pour chaque page, basculez le switch Published dans l'en-tête supérieur sur
ON. Les pages apparaissent dans la barre latérale sous une section APPLICATION
(regroupées par catégorie). Ajoutez une Company, puis un Contact lié à cette Company, puis
un Deal, confirmez que les relations se rendent correctement. La page Companies devrait
maintenant afficher 1 dans contact_count.
company produira. Note : la section APPLICATION de la barre latérale apparaît dès que vous publiez, les pages sont regroupées sous un en-tête de catégorie (ici : CUSTOMER PORTAL). Le panneau de détail rend un fil d'Ariane, un bouton Edit (en haut à droite), des icônes crayon par champ pour les édits en ligne, des onglets supplémentaires en haut (Details / Vehicles / etc.) et, sous le formulaire, une section de grille liée tirant des lignes d'un autre BE filtrées par l'enregistrement courant. Le badge cloche ("1") indique qu'il y a un échec Event Log non lu. Si quelque chose paraît vide : la cause la plus courante est d'avoir oublié
de basculer le switch Published, s'il est OFF, naviguer vers /companies dans
la barre latérale n'affiche rien. Basculez-le sur ON et rafraîchissez.
Étape 4 - Nettoyez un champ à l'enregistrement avec un Business Event (5 min)
Les Business Events exécutent un peu de logique dès qu'un enregistrement est écrit. Le plus
petit utile : retirer les espaces superflus du title d'un deal à chaque
sauvegarde, pour que " Acme renewal " devienne "Acme renewal".
Mettons cela en place.
Vous n'avez jamais besoin d'une règle pourcreated_at/updated_at/created_by/updated_by. Archestack ajoute ces quatre colonnes d'audit à chaque table et les estampille à chaque insertion et update automatiquement, donc un trigger "estampiller updated_at" ne ferait que dupliquer un travail que la plateforme fait déjà.
- Ouvrez Business Events -> Create.
- Rule Name :
Normalize deal.title. Basculez Enabled sur ON. - Business Entity :
deal. Triggers : cochez Before Update. - Pas de conditions, se déclenche à chaque update.
-
Ajoutez une action : choisissez Execute Script. Le corps du script de
l'action :
string title = Entity.title; Entity.title = title?.Trim(); -
Cliquez sur l'onglet Simulate -> choisissez n'importe quel Deal
existant -> cliquez sur Run Simulation. Le panneau de sortie affiche
que le script s'est exécuté avec succès et la valeur sur laquelle
Entity.titlea été réglé. (Aucune écriture réelle n'a lieu, la simulation tourne dans une transaction rollback.) - Enregistrez. Testez en éditant un Deal dont le titre comporte des espaces en début ou fin, le titre devrait revenir nettoyé à chaque sauvegarde.
Pourquoi Before Update + Execute Script au lieu d'une action qui
"définit un champ" ? Archestack n'a pas d'action discrète "Set field". La façon de
muter un enregistrement depuis un trigger est d'affecter Entity.column_name à
l'intérieur d'un Execute Script sur un timing Before*. La plateforme persiste
l'Entity modifiée dans le cadre de l'écriture en cours, pas de requête supplémentaire, pas
de risque de récursion.
Étape 5 - Recalculez company.open_deal_value avec un Script Module (10 min)
Un total calculé comme "valeur des deals ouverts par company" est trop dynamique pour qu'une colonne stockée reste précise à la main. Nous la garderons en sync avec un petit script qui se réexécute chaque fois qu'un deal sur une company est inséré, mis à jour ou supprimé.
Écrivez le script
Ouvrez Script Modules -> Create. Nommez-le
RecalcCompanyOpenDealValue.
Basculez sur l'onglet Parameters. Cliquez sur Add. Name
company_id, Type int, Required coché.
Retour à l'onglet Edit. Corps :
var openDeals = await Db.From("deal")
.Where("company_id", "=", company_id)
.Where("stage", "!=", "Won")
.Where("stage", "!=", "Lost")
.ToListAsync();
decimal total = 0;
foreach (var d in openDeals) total += (decimal)(d.amount ?? 0);
await Db.UpdateAsync("company", company_id, new { open_deal_value = total });
return total;
Basculez sur l'onglet Test. Tapez un vrai company_id de votre
page Companies dans la saisie du paramètre, cliquez sur Run. La sortie
affiche la valeur de retour (le total) et un indicateur de succès. Rechargez la page
Companies, open_deal_value sur cette company est maintenant en sync.
Trébuchements courants :
- Oublier
awaitsur.ToListAsync(), le script compile maisopenDealsfinit par contenir uneTask, pas les lignes. L'erreur dans le panneau Test mentionnera "cannot be enumerated".- Utiliser
Db.Queryau lieu deDb.From, il n'y a pas de méthodeQuery. Restez surDb.From(table)pour les requêtes chaînées etDb.GetAsync(table, id)pour les lectures d'une seule ligne par PK.- Essayer
.SumAsync(...), n'existe pas. Récupérez + sommez en C#, comme ci-dessus.
Câblez-le à un Business Event
- Ouvrez Business Events -> Create.
- Rule Name :
Recalc company.open_deal_value on deal change. Enabled ON. - Business Entity :
deal. Triggers : cochez After Create, After Update et Before Delete. - Pas de conditions, se déclenche à chaque changement.
-
Ajoutez une action : Execute Script. Corps :
var entity = OldEntity != null && OldEntity.company_id != null ? OldEntity // for delete & update, OldEntity has the original company_id : Entity; // for create, only Entity is populated await Modules.CallAsync("RecalcCompanyOpenDealValue", new Dictionary<string, object?> { ["company_id"] = entity.company_id }); - Enregistrez. Éditez l'
amountd'un Deal et rechargez la page Company, le total reste en sync.
Pourquoi l'appeler via Modules au lieu d'inliner la logique dans le script de ce trigger ? Deux raisons. D'abord, le recalcul est réutilisable, vous pouvez aussi l'appeler depuis un Scheduled Event (rattrapage nocturne) ou directement depuis le front-end. Ensuite, cela isole la logique dans un seul endroit nommé, plus facile à tester, plus facile à retrouver plus tard.
Étape 6 - Regroupez-le en package (5 min)
Vous avez construit un vrai mini-CRM fonctionnel. Regroupez-le pour pouvoir le déplacer vers un autre environnement.
- Ouvrez Packages -> Create. Nommez-le
MiniCRM v1. - Ajoutez les trois Pages. Ouvrez le panneau des éléments liés pour voir la cascade : le package tirera les BE que les pages référencent, les tables sources dans lesquelles ces BE lisent, plus les deux Business Events et le Script Module qui les touchent.
- Décochez tout ce que vous préféreriez omettre, en général vous garderiez tout pour un tutoriel comme celui-ci.
- Cliquez sur Export -> téléchargez le ZIP.
Importer le ZIP dans un environnement différent recrée tout (en supposant que le schéma est déployé d'abord). C'est ainsi que les partenaires livrent des configurations spécifiques à un vertical et c'est ainsi que vous promouvriez le travail d'un essai à un environnement payant plus tard.
Récap - ce qui vient de se passer
- Vous avez conçu un schéma, l'avez déployé comme de vraies tables PostgreSQL, et n'avez jamais écrit de SQL à la main.
- Vous avez exposé ces tables à travers des Business Entities, en choisissant ce qu'il faut exposer, en joignant des libellés pour la convivialité, en agrégeant les comptages de lignes liées.
- Vous avez composé trois pages à partir de configuration seule, avec onglets embarqués et recherche.
- Vous avez ajouté un comportement avec un Business Event (auto-estampillage) et un calcul plus complexe avec un Script Module, huit lignes de C# qui s'exécutent chaque fois que les données changent.
- Vous avez regroupé l'ensemble en Package, portable entre environnements.
La même boucle s'étend à des dizaines de tables et des centaines de pages. La Reference couvre le reste de la plateforme, branding, sync de données tiers, scaffolding assisté par IA, mais la compétence centrale est ce que vous venez d'apprendre.
Pour aller plus loin
- Generate PDF invoices - ajoutez un template de facture imprimable aux Deals que vous venez de construire. Mêmes données, nouveau canal de sortie.
- Scheduled Events - étendez le script de recalcul avec un rattrapage nocturne qui tourne sans action utilisateur.
- AI Builder - pointez-le sur "ajouter le support des rendez-vous de service, date, customer, vehicle, status" et regardez-le échafauder une fonctionnalité similaire.