Tutorial · 45 Min

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), erforderlich
  • industry · VARCHAR(80), nullable
  • open_deal_value · DECIMAL, Default 0 - wird über das Skript in Schritt 5 synchron gehalten
  • created_at · TIMESTAMPTZ, Default now()

Tabelle: contact

  • id · SERIAL, PK (auto)
  • first_name, last_name · VARCHAR(100), erforderlich
  • email · VARCHAR(200), erforderlich, UQ an
  • company_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 eingerichtet
  • title · VARCHAR(200), erforderlich
  • stage · VARCHAR(40), erforderlich, Default 'New' - Werte: New, Qualified, Proposal, Won, Lost
  • amount · DECIMAL, erforderlich, Default 0
  • created_at, updated_at · TIMESTAMPTZ, Default now()

Fremdschlüssel

Fremdschlüssel leben im Relations-Tab des rechten Panels. Wähle die contact-Tabelle, wechsle zu Relations, scrolle zum "Add relationship"-Abschnitt:

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

Wiederhole für die deal-Tabelle (ihre company_idcompany.id).

Schema Designer-Canvas mit mehreren Tabellen, verbunden durch FK-Linien. Die Symbolleiste oben hat Canvas/JSON-Tabs, ein Packages-Dropdown, eine Find-table-Suche, Zoom-Controls und rechts: + (Add Table), Add Group, Add Text, Save, JSON/Code, Deploy (grüne Rakete).
So sieht der Canvas aus, sobald du ein paar Tabellen mit Beziehungen hast. Die Symbolleiste (oben) hat Canvas / JSON-Tabs, einen Packages-Filter, eine Find-table-Suche, Zoom-Controls und rechts die Icon-Buttons + (Add Table), Add Group, Add Text, Save, JSON und die grüne Deploy-Rakete. FK-Spalten zeigen ein Kettengliedsymbol neben ihrem Typ, und die Beziehungen rendern als farbige Linien zwischen Tabellen. (Das Beispiel hier nutzt andere Tabellen als dieses Tutorial, dein 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, dass company vor contact und deal erstellt 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 column company.id, To column contact.company_id. Schalte Aggregate Mode ein. Wähle Spalte id, setze Aggregate Function = COUNT, nenne sie contact_count.
  • Join zu deal: From column company.id, To column deal.company_id. Aggregate Mode an. Wähle Spalte id, Funktion COUNT, nenne sie open_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:

  1. Companies - Page Name Companies, Page Route /companies, Business Entity company. 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.
  2. Contacts - Page Name Contacts, Page Route /contacts, Business Entity contact. Das automatisch erzeugte company_id-Feld des Detailformulars erscheint als Zahl, ändere seinen Type auf Select und setze das Entity-Autocomplete auf company, damit Nutzer eine Company per Name auswählen.
  3. Deals - Page Name Deals, Page Route /deals, Business Entity deal. Gleiche Behandlung für company_id (Type Select, Entity company). Für stage lass Type vorerst als Text, die Werte können später über ein Validate-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.

Veröffentlichte Endbenutzer-Page mit Breadcrumbs (Home / Customers / Record #9), einem Edit-Button oben rechts, mehreren Tabs (Details, Vehicles, Address & Contact, Open service orders, Closed Service orders), einem Properties-Formular mit Inline-Edit-Stiften pro Feld und einem zugehörigen Datengrid (Vehicles) unter dem Formular.
So sieht eine veröffentlichte Page zur Laufzeit aus. Das Beispiel hier ist eine Customer-Page aus einer anderen Domain (DMS), aber die Form ist genau das, was deine 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ür created_at / updated_at / created_by / updated_by brauchst 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.
  1. Öffne Business EventsCreate.
  2. Rule Name: Normalize deal.title. Schalte Enabled AN.
  3. Business Entity: deal. Triggers: Kreuze Before Update an.
  4. Keine Conditions, feuere auf jedem Update.
  5. Füge eine Action hinzu: Wähle Execute Script. Der Skript-Body der Action:
    string title = Entity.title;
    Entity.title = title?.Trim();
  6. 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.title gesetzt wurde. (Kein echter Write passiert, die Simulation läuft in einer zurückgerollten Transaktion.)
  7. 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 ModulesCreate. 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 await auf .ToListAsync() zu vergessen, das Skript kompiliert, aber openDeals hält am Ende einen Task, nicht die Zeilen. Der Fehler im Test-Panel erwähnt "cannot be enumerated".
  • Db.Query statt Db.From zu verwenden, es gibt keine Query-Methode. Bleib bei Db.From(table) für verkettete Queries und Db.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

  1. Öffne Business EventsCreate.
  2. Rule Name: Recalc company.open_deal_value on deal change. Enabled AN.
  3. Business Entity: deal. Triggers: Kreuze After Create, After Update und Before Delete an.
  4. Keine Conditions, feuere auf jeder Änderung.
  5. 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
    });
  6. Speichern. Bearbeite den amount eines 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.

  1. Öffne PackagesCreate. Benenne es MiniCRM v1.
  2. 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.
  3. Hake alles ab, was du lieber weglassen würdest, normalerweise würdest du für ein Tutorial wie dieses alles behalten.
  4. 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.