Tutoriel · 45 min

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), requis
  • industry · VARCHAR(80), nullable
  • open_deal_value · DECIMAL, défaut 0 - tenu à jour via le script de l'étape 5
  • created_at · TIMESTAMPTZ, défaut now()

Table : contact

  • id · SERIAL, PK (auto)
  • first_name, last_name · VARCHAR(100), requis
  • email · 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 ensuite
  • title · VARCHAR(200), requis
  • stage · VARCHAR(40), requis, défaut 'New' - valeurs : New, Qualified, Proposal, Won, Lost
  • amount · DECIMAL, requis, défaut 0
  • created_at, updated_at · TIMESTAMPTZ, défaut now()

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" :

  1. FK column (on this table) = company_id
  2. Relation type = One-to-Many
  3. References -> table = company
  4. References -> column = id
  5. Cliquez sur Add Relationship.

Répétez pour la table deal (son company_id -> company.id).

Canevas du Schema Designer avec plusieurs tables connectées par des lignes de FK. La barre d'outils en haut a les onglets Canvas/JSON, le menu déroulant Packages, la recherche Find table, les contrôles de zoom et à droite : + (Add Table), Add Group, Add Text, Save, JSON/code, Deploy (fusée verte).
À quoi ressemble le canevas une fois que vous avez quelques tables avec relations. La barre d'outils (en haut) a les onglets Canvas / JSON, un filtre Packages, une recherche Find table, des contrôles de zoom et, à droite, les boutons-icônes + (Add Table), Add Group, Add Text, Save, JSON et la Deploy fusée verte. Les colonnes FK affichent une icône de chaîne à côté de leur type et les relations se rendent comme des lignes colorées entre les tables. (L'exemple montré utilise des tables différentes de ce tutoriel, votre configuration 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 que company soit créée avant contact et deal, 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 column company.id, To column contact.company_id. Basculez Aggregate Mode. Choisissez la colonne id, réglez Aggregate Function = COUNT, nommez-la contact_count.
  • Jointure vers deal : From column company.id, To column deal.company_id. Aggregate Mode activé. Choisissez la colonne id, fonction COUNT, nommez-la open_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 :

  1. Companies - Page Name Companies, Page Route /companies, Business Entity company. 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.
  2. Contacts - Page Name Contacts, Page Route /contacts, Business Entity contact. Le champ company_id du formulaire de détail auto-généré apparaîtra comme un nombre, changez son Type en Select et réglez l'autocomplete Entity sur company pour que les utilisateurs choisissent une company par nom.
  3. Deals - Page Name Deals, Page Route /deals, Business Entity deal. Même traitement pour company_id (Type Select, Entity company). Pour stage, laissez le Type sur Text pour l'instant, les valeurs peuvent être imposées via un event Validate plus 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.

Page publiée côté utilisateur final avec fil d'Ariane (Home / Customers / Record #9), un bouton Edit en haut à droite, plusieurs onglets (Details, Vehicles, Address & Contact, Open service orders, Closed Service orders), un formulaire Properties avec des crayons d'édition en ligne par champ et une grille de données liées (Vehicles) sous le formulaire.
À quoi ressemble une page publiée à l'exécution. L'exemple ici est une page Customer d'un autre domaine (DMS), mais la forme est exactement ce que votre page 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 pour created_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à.
  1. Ouvrez Business Events -> Create.
  2. Rule Name : Normalize deal.title. Basculez Enabled sur ON.
  3. Business Entity : deal. Triggers : cochez Before Update.
  4. Pas de conditions, se déclenche à chaque update.
  5. Ajoutez une action : choisissez Execute Script. Le corps du script de l'action :
    string title = Entity.title;
    Entity.title = title?.Trim();
  6. 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.title a été réglé. (Aucune écriture réelle n'a lieu, la simulation tourne dans une transaction rollback.)
  7. 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 await sur .ToListAsync(), le script compile mais openDeals finit par contenir une Task, pas les lignes. L'erreur dans le panneau Test mentionnera "cannot be enumerated".
  • Utiliser Db.Query au lieu de Db.From, il n'y a pas de méthode Query. Restez sur Db.From(table) pour les requêtes chaînées et Db.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

  1. Ouvrez Business Events -> Create.
  2. Rule Name : Recalc company.open_deal_value on deal change. Enabled ON.
  3. Business Entity : deal. Triggers : cochez After Create, After Update et Before Delete.
  4. Pas de conditions, se déclenche à chaque changement.
  5. 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
    });
  6. Enregistrez. Éditez l'amount d'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.

  1. Ouvrez Packages -> Create. Nommez-le MiniCRM v1.
  2. 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.
  3. Décochez tout ce que vous préféreriez omettre, en général vous garderiez tout pour un tutoriel comme celui-ci.
  4. 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.