Baue deine erste App
Am Ende dieses Tutorials wirst du ein kleines CRM mit drei Tabellen (company,
contact, deal) haben, einer Listen-und-Detail-Page für jede, einem
Business Event, das updated_at des Deals automatisch stempelt, und einem Script
Module, das den Gesamtwert offener Deals eines Company-Datensatzes neu berechnet, wann immer
sich ein Deal ändert. Jedes Konzept aus Kernkonzepte taucht
mindestens einmal auf.
Es setzt voraus, dass du Erste Schritte abgeschlossen hast und eine offene Archestack-Umgebung in einem anderen Tab hast. Gesamtdauer: etwa 45 Minuten, wenn du sorgfältig liest, 25, wenn du überfliegst.
Hier verwendete Konventionen: Tabellen- und Spaltennamen sind snake_case (die
Plattform deutet diese Konvention im "Table name"-Feld des Schema Designers an). Code-Beispiele
nutzen die echte Db-API, siehe Reference →
Script-aufrufbare APIs für die volle Oberfläche.
Schritt 1 - Das Schema entwerfen (10 Min)
Öffne Schema Designer und füge drei Tabellen hinzu.
Für jede Tabelle: Klicke in der Symbolleiste auf Add Table, tippe den Namen in
das "Table name"-Feld des Dialogs, klicke auf Create. Die neue Tabelle
erscheint auf dem Canvas mit einem automatisch generierten id SERIAL
Primärschlüssel (gesperrt). Das rechte Panel öffnet sich auf dem Columns-Tab, füge zusätzliche
Spalten aus der gestrichelten "Add column"-Box unten hinzu. Klicke jede Spalte an, um sie
auszuklappen und die Schalter (PK / NULL / UQ), die Length und den Default-Wert zu justieren.
Tabelle: company
id· SERIAL, PK (auto)name· VARCHAR(200), erforderlichindustry· VARCHAR(80), nullableopen_deal_value· DECIMAL, Default0- wird über das Skript in Schritt 5 synchron gehaltencreated_at· TIMESTAMPTZ, Defaultnow()
Tabelle: contact
id· SERIAL, PK (auto)first_name,last_name· VARCHAR(100), erforderlichemail· VARCHAR(200), erforderlich, UQ ancompany_id· INTEGER, erforderlich - Fremdschlüssel, wird als nächstes eingerichtet
Tabelle: deal
id· SERIAL, PK (auto)company_id· INTEGER, erforderlich - Fremdschlüssel, wird als nächstes eingerichtettitle· VARCHAR(200), erforderlichstage· VARCHAR(40), erforderlich, Default'New'- Werte: New, Qualified, Proposal, Won, Lostamount· DECIMAL, erforderlich, Default0created_at,updated_at· TIMESTAMPTZ, Defaultnow()
Fremdschlüssel
Fremdschlüssel leben im Relations-Tab des rechten Panels. Wähle die
contact-Tabelle, wechsle zu Relations, scrolle zum "Add relationship"-Abschnitt:
- FK column (on this table) =
company_id - Relation type =
One-to-Many - References → table =
company - References → column =
id - Klicke auf Add Relationship.
Wiederhole für die deal-Tabelle (ihre company_id → company.id).
company / contact / deal-Setup wird die gleiche Form mit zwei FK-Linien haben, die auf company zeigen.)Deployen
Klicke auf Deploy in der Schema Designer-Symbolleiste. Du landest auf der
Deployment-Konfigurationsseite. Wechsle zum Generated SQL-Tab, du solltest
drei CREATE TABLE-Statements plus die FK-Constraints sehen. Klicke oben rechts
auf Deploy.
Was du siehst, wenn es funktioniert: Die Deployment-Zeile in Database Deployments → Overview geht innerhalb weniger Sekunden von "Executing" zu "Succeeded". Die drei Tabellen erscheinen in der Tabellenliste des Object Browser.
Wenn das Deploy an FK-Constraints fehlschlägt: Das Generated SQL gibt Tabellen in der Reihenfolge aus, in der sie gespeichert wurden. Öffne den SQL-Tab und ordne so, dasscompanyvorcontactunddealerstellt wird, dann deploye neu. Oder klicke auf Regenerate, nachdem du die Tabellen im Schema Designer-Canvas manuell umsortiert hast.
Schritt 2 - Die Business Entities komponieren (10 Min)
Jede Page braucht eine Business Entity, an die sie bindet. Öffne Business Entities und klicke für jede auf Create. Drücke Run Preview nach jedem Speichern, um zu bestätigen, dass die Spalten korrekt zurückkommen.
BE: company
Entity Name company, Master Table company, Label Column
name. Speichern.
Klicke zweimal auf Add Join, um zwei aggregierte Joins hinzuzufügen:
-
Join zu
contact: From columncompany.id, To columncontact.company_id. Schalte Aggregate Mode ein. Wähle Spalteid, setze Aggregate Function =COUNT, nenne siecontact_count. -
Join zu
deal: From columncompany.id, To columndeal.company_id. Aggregate Mode an. Wähle Spalteid, FunktionCOUNT, nenne sieopen_deal_count. (Wir filtern in Schritt 5 über ein Business Event auf "offene" Deals; vorerst zählt das alle Deals.)
BE: contact
Entity Name contact, Master Table contact, Label Column
email. Füge einen Join zu company über
contact.company_id → company.id hinzu, gib die name-Spalte als
company_name heraus. (Aktiviere Aggregate Mode hier nicht, es ist ein normaler Join.)
BE: deal
Entity Name deal, Master Table deal, Label Column
title. Füge einen Join zu company über
deal.company_id → company.id hinzu, gib die name-Spalte als
company_name heraus.
Verifizieren: Klicke auf jeder BE auf Run Preview. Du siehst ein leeres Grid (noch keine Daten) mit den Spaltenüberschriften, die du definiert hast. Wenn eine Join-Spaltenüberschrift fehlt, hast du wahrscheinlich vergessen, die Spalte im Spalten-Picker des Joins anzukreuzen. Öffne den Join erneut, kreuze die Spalte an, speichere, lass Run Preview erneut laufen.
Schritt 3 - Die Pages bauen (10 Min)
Öffne Page Editor. Für jede Business Entity klicke auf Create:
- Companies - Page Name
Companies, Page Route/companies, Business Entitycompany. Der Visual-Tab erzeugt automatisch eine Liste und ein Detailformular. Im Detailformular klicke zweimal auf Add Tab, um Contacts- und Deals-Tabs hinzuzufügen. In jedem Tab füge eine RelatedGrid-Sektion hinzu, gebunden an die contact / deal BE mit dem Join-Filter company.id = current record's id. - Contacts - Page Name
Contacts, Page Route/contacts, Business Entitycontact. Das automatisch erzeugtecompany_id-Feld des Detailformulars erscheint als Zahl, ändere seinen Type auf Select und setze das Entity-Autocomplete aufcompany, damit Nutzer eine Company per Name auswählen. - Deals - Page Name
Deals, Page Route/deals, Business Entitydeal. Gleiche Behandlung fürcompany_id(TypeSelect, Entitycompany). Fürstagelass Type vorerst alsText, die Werte können später über einValidate-Event erzwungen werden, wenn du willst.
Für jede Page schalte den Published-Switch im oberen Header auf AN. Die
Pages erscheinen in der Sidebar unter einem APPLICATION-Abschnitt (gruppiert
nach Kategorie). Füge eine Company hinzu, dann einen Contact, der mit dieser Company verbunden
ist, dann einen Deal, bestätige, dass die Beziehungen korrekt rendern. Die Companies-Page sollte
jetzt 1 in contact_count zeigen.
company-Page produzieren wird. Hinweis: Der APPLICATION-Abschnitt der Sidebar erscheint, sobald du veröffentlichst, Pages sind unter einem Kategorie-Header gruppiert (hier: CUSTOMER PORTAL). Das Detail-Panel rendert Breadcrumbs, einen Edit-Button (oben rechts), Stift-Icons pro Feld für Inline-Edits, zusätzliche Tabs quer oben (Details / Vehicles / etc.) und unter dem Formular eine Related-Grid-Sektion, die Zeilen aus einer anderen BE zieht, gefiltert nach dem aktuellen Datensatz. Das Glockensymbol-Badge ("1") zeigt, dass es einen ungelesenen Event Log-Fehler gibt. Wenn etwas leer aussieht: Die häufigste Ursache ist, den Published-Switch
nicht umgelegt zu haben, wenn er AUS ist, zeigt die Navigation zu /companies in
der Sidebar nichts. Schalte ihn AN und aktualisiere.
Schritt 4 - Ein Feld beim Speichern bereinigen mit einem Business Event (5 Min)
Business Events führen ein bisschen Logik aus, sobald ein Datensatz geschrieben wird. Das
kleinste sinnvolle: überflüssige Leerzeichen aus dem title eines Deals bei jedem
Speichern trimmen, sodass " Acme renewal " als "Acme renewal" landet.
Richten wir das ein.
Fürcreated_at/updated_at/created_by/updated_bybrauchst du nie eine Regel. Archestack fügt diese vier Audit-Spalten automatisch zu jeder Tabelle hinzu und stempelt sie bei jedem Insert und Update, ein "updated_at stempeln"-Trigger würde also nur Arbeit duplizieren, die die Plattform bereits erledigt.
- Öffne Business Events → Create.
- Rule Name:
Normalize deal.title. Schalte Enabled AN. - Business Entity:
deal. Triggers: Kreuze Before Update an. - Keine Conditions, feuere auf jedem Update.
-
Füge eine Action hinzu: Wähle Execute Script. Der Skript-Body der Action:
string title = Entity.title; Entity.title = title?.Trim(); -
Klicke auf den Simulate-Tab → wähle einen beliebigen bestehenden Deal →
klicke auf Run Simulation. Das Ausgabe-Panel zeigt, dass das Skript
erfolgreich lief und worauf
Entity.titlegesetzt wurde. (Kein echter Write passiert, die Simulation läuft in einer zurückgerollten Transaktion.) - Speichern. Teste, indem du einen Deal mit führenden oder nachgestellten Leerzeichen im Titel bearbeitest, der Titel sollte bei jedem Speichern getrimmt zurückkommen.
Warum Before Update + Execute Script statt einer Action, die "ein
Feld setzt"? Archestack hat keine separate "Set field"-Action. Die Art, einen
Datensatz aus einem Trigger zu mutieren, ist, in einem Execute Script auf einem
Before*-Timing Entity.column_name zuzuweisen. Die Plattform persistiert die
modifizierte Entity als Teil des laufenden Schreibvorgangs zurück, keine zusätzliche Query,
kein Rekursionsrisiko.
Schritt 5 - company.open_deal_value mit einem Script Module neu berechnen (10 Min)
Eine berechnete Summe wie "open deal value pro Company" ist zu dynamisch, als dass eine gespeicherte Spalte von Hand korrekt bleiben könnte. Wir halten sie mit einem kleinen Skript synchron, das jedes Mal neu läuft, wenn ein Deal einer Company eingefügt, aktualisiert oder gelöscht wird.
Das Skript schreiben
Öffne Script Modules → Create. Benenne es
RecalcCompanyOpenDealValue.
Wechsle zum Parameters-Tab. Klicke auf Add. Name
company_id, Type int, Required angekreuzt.
Zurück zum Edit-Tab. Body:
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;
Wechsle zum Test-Tab. Tippe eine echte company_id aus deiner
Companies-Page in die Parameter-Eingabe, klicke auf Run. Die Ausgabe zeigt
den Rückgabewert (die Summe) und einen Erfolgs-Indikator. Lade die Companies-Page neu,
open_deal_value auf dieser Company ist jetzt synchron.
Häufige Stolpersteine:
- Das
awaitauf.ToListAsync()zu vergessen, das Skript kompiliert, aberopenDealshält am Ende einenTask, nicht die Zeilen. Der Fehler im Test-Panel erwähnt "cannot be enumerated".Db.QuerystattDb.Fromzu verwenden, es gibt keineQuery-Methode. Bleib beiDb.From(table)für verkettete Queries undDb.GetAsync(table, id)für Single-Row-per-PK..SumAsync(...)zu versuchen, existiert nicht. Fetch + Summe in C#, wie oben.
An ein Business Event verdrahten
- Öffne Business Events → Create.
- Rule Name:
Recalc company.open_deal_value on deal change. Enabled AN. - Business Entity:
deal. Triggers: Kreuze After Create, After Update und Before Delete an. - Keine Conditions, feuere auf jeder Änderung.
-
Füge eine Action hinzu: Execute Script. Body:
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 }); - Speichern. Bearbeite den
amounteines Deals und lade die Company-Page neu, die Summe bleibt synchron.
Warum es über Modules aufrufen, statt die Logik in das Skript dieses Triggers inline zu setzen? Zwei Gründe. Erstens ist die Neuberechnung wiederverwendbar, du kannst sie auch von einem Scheduled Event (nächtlicher Nachzieher) oder direkt aus dem Frontend aufrufen. Zweitens isoliert es die Logik an einem einzigen benannten Ort, leichter zu testen, leichter später wiederzufinden.
Schritt 6 - Als Package bündeln (5 Min)
Du hast ein echtes, funktionierendes Mini-CRM gebaut. Bündle es, damit du es in eine andere Umgebung verschieben kannst.
- Öffne Packages → Create. Benenne es
MiniCRM v1. - Füge die drei Pages hinzu. Öffne das Linked-Items-Panel, um die Kaskade zu sehen: Das Package zieht die BEs, an die die Pages binden, die Quelltabellen, aus denen die BEs lesen, plus die zwei Business Events und das Script Module, die sie berühren, mit.
- Hake alles ab, was du lieber weglassen würdest, normalerweise würdest du für ein Tutorial wie dieses alles behalten.
- Klicke auf Export → lade das ZIP herunter.
Das ZIP in eine andere Umgebung zu importieren stellt alles wieder her (vorausgesetzt, das Schema ist zuerst deployt). So liefern Partner branchenspezifische Konfigurationen aus und so würdest du später Arbeit von einem Trial in eine bezahlte Umgebung übernehmen.
Recap - was gerade passiert ist
- Du hast ein Schema entworfen, es als echte PostgreSQL-Tabellen deployt und nie SQL von Hand geschrieben.
- Du hast diese Tabellen durch Business Entities freigegeben, gewählt, was sichtbar wird, Labels zur Benutzbarkeit eingefügt, verwandte Zeilen gezählt.
- Du hast drei Pages allein aus Konfiguration komponiert, mit eingebetteten Tabs und Suche.
- Du hast Verhalten mit einem Business Event hinzugefügt (Auto-Stempeln) und eine komplexere Berechnung mit einem Script Module, acht Zeilen C#, die jedes Mal laufen, wenn sich die Daten ändern.
- Du hast das Ganze als Package gebündelt, portabel über Umgebungen.
Derselbe Kreislauf skaliert auf Dutzende Tabellen und Hunderte Pages. Die Reference deckt den Rest der Plattform ab, Branding, Third-Party-Datensync, KI-gestütztes Scaffolding, aber die Kernkompetenz ist das, was du gerade gelernt hast.
Wie es weitergeht
- PDF-Rechnungen erzeugen - füge eine druckbare Rechnungsvorlage zu den Deals hinzu, die du gerade gebaut hast. Gleiche Daten, neuer Ausgabekanal.
- Scheduled Events - erweitere das Recompute-Skript mit einem nächtlichen Nachzieher, der ohne Nutzeraktion läuft.
- AI Assistant - bitte ihn um "support for service appointments, date, customer, vehicle, status" und sieh zu, wie er eine ähnliche Funktion scaffolded.