Construa a sua primeira aplicação
No fim deste tutorial terá um pequeno CRM com três tabelas (company,
contact, deal), uma página de lista-e-detalhe para cada, um Business
Event que carimba automaticamente o updated_at do deal, e um Script Module que
recalcula o valor total dos deals em aberto da empresa sempre que um deal muda. Todos os
conceitos de Conceitos centrais aparecem pelo menos uma vez.
Assume que terminou Começar e que tem um ambiente Archestack aberto noutra aba. Tempo total: cerca de 45 minutos se ler com atenção, 25 se ler por alto.
Convenções usadas aqui: nomes de tabela e coluna em snake_case (a plataforma
dá a dica desta convenção no campo "Table name" do Schema Designer). Os exemplos de código usam
a API Db real, ver Referência → APIs
invocáveis a partir de scripts para a superfície completa.
Passo 1 - Desenhar o schema (10 min)
Abra Schema Designer e adicione três tabelas.
Para cada tabela: clique em Add Table na barra de ferramentas, escreva o nome
no campo "Table name" do diálogo, clique em Create. A nova tabela aparece no
canvas com uma chave primária SERIAL id gerada automaticamente (bloqueada). O
painel à direita abre no separador Columns, adicione colunas adicionais a partir da caixa
tracejada "Add column" no fundo. Clique em cada coluna para a expandir e ajustar os
interruptores (PK / NULL / UQ), o Length e o Default value.
Tabela: company
id· SERIAL, PK (auto)name· VARCHAR(200), obrigatórioindustry· VARCHAR(80), nullableopen_deal_value· DECIMAL, default0- mantido em sincronização via o script no Passo 5created_at· TIMESTAMPTZ, defaultnow()
Tabela: contact
id· SERIAL, PK (auto)first_name,last_name· VARCHAR(100), obrigatórioemail· VARCHAR(200), obrigatório, UQ ligadocompany_id· INTEGER, obrigatório - chave estrangeira, configurada a seguir
Tabela: deal
id· SERIAL, PK (auto)company_id· INTEGER, obrigatório - chave estrangeira, configurada a seguirtitle· VARCHAR(200), obrigatóriostage· VARCHAR(40), obrigatório, default'New'- valores: New, Qualified, Proposal, Won, Lostamount· DECIMAL, obrigatório, default0created_at,updated_at· TIMESTAMPTZ, defaultnow()
Chaves estrangeiras
As chaves estrangeiras vivem no separador Relations do painel à direita.
Selecione a tabela contact, mude para Relations, desça até à secção "Add
relationship":
- FK column (on this table) =
company_id - Relation type =
One-to-Many - References → table =
company - References → column =
id - Clique em Add Relationship.
Repita para a tabela deal (o seu company_id → company.id).
company / contact / deal terá a mesma forma com duas linhas FK a apontar para company.)Deploy
Clique em Deploy na barra de ferramentas do Schema Designer. Aterra na página
de configuração do deployment. Mude para o separador Generated SQL, deverá ver
três statements CREATE TABLE mais as constraints FK. Clique em
Deploy no canto superior direito.
O que verá se funcionar: a linha do deployment em Database Deployments → Overview passa de "Executing" para "Succeeded" dentro de alguns segundos. As três tabelas aparecem na lista de tabelas do Object Browser.
Se o deploy falhar em constraints FK: o Generated SQL emite tabelas pela ordem em que foram guardadas. Abra o separador SQL e reordene para quecompanyseja criada antes decontactedeal, depois faça redeploy. Ou clique em Regenerate após reordenar manualmente as tabelas no canvas do Schema Designer.
Passo 2 - Compor as Business Entities (10 min)
Cada página precisa de uma Business Entity a que se ligar. Abra Business Entities e clique em Create para cada uma. Carregue em Run Preview após cada gravação para confirmar que as colunas voltam corretamente.
BE: company
Entity Name company, Master Table company, Label Column
name. Guarde.
Clique em Add Join duas vezes para adicionar dois joins agregados:
-
Join a
contact: From columncompany.id, To columncontact.company_id. Ative Aggregate Mode. Escolha a colunaid, defina Aggregate Function =COUNT, nomeie-acontact_count. -
Join a
deal: From columncompany.id, To columndeal.company_id. Aggregate Mode ligado. Escolha a colunaid, funçãoCOUNT, nomeie-aopen_deal_count. (Vamos filtrar para deals "em aberto" via um Business Event no Passo 5; por agora isto conta todos os deals.)
BE: contact
Entity Name contact, Master Table contact, Label Column
email. Adicione um Join a company via
contact.company_id → company.id, expondo a coluna name como
company_name. (Não ative Aggregate Mode aqui, é um join normal.)
BE: deal
Entity Name deal, Master Table deal, Label Column
title. Adicione um Join a company via
deal.company_id → company.id, expondo a coluna name como
company_name.
Verifique: em cada BE, clique em Run Preview. Verá uma grelha vazia (ainda sem dados) com os cabeçalhos de coluna que definiu. Se um cabeçalho de coluna de join está em falta, provavelmente esqueceu-se de marcar a coluna no seletor de colunas do join. Reabra o join, marque a coluna, guarde, corra a pré-visualização novamente.
Passo 3 - Construir as páginas (10 min)
Abra Page Editor. Para cada Business Entity, clique em Create:
- Companies - Page Name
Companies, Page Route/companies, Business Entitycompany. O separador Visual gera automaticamente uma lista e um formulário de detalhe. No formulário de detalhe, clique em Add Tab duas vezes para adicionar os separadores Contacts e Deals. Em cada separador, adicione uma secção RelatedGrid ligada à BE contact / deal com o filtro de join definido para company.id = id do registo atual. - Contacts - Page Name
Contacts, Page Route/contacts, Business Entitycontact. O campocompany_iddo formulário de detalhe gerado automaticamente aparecerá como número, mude o seu Type para Select e defina o autocomplete Entity paracompanypara que os utilizadores escolham uma empresa pelo nome. - Deals - Page Name
Deals, Page Route/deals, Business Entitydeal. Mesmo tratamento paracompany_id(TypeSelect, Entitycompany). Parastage, deixe o Type comoTextpor agora, os valores podem ser impostos via um eventoValidatemais tarde se quiser.
Para cada página, vire o interruptor Published no cabeçalho do topo para ON.
As páginas aparecem na barra lateral sob uma secção APPLICATION (agrupadas por
categoria). Adicione uma Company, depois um Contact ligado a essa Company, depois um Deal,
confirme que as relações renderizam corretamente. A página Companies deverá agora mostrar
1 em contact_count.
company produzirá. Nota: a secção APPLICATION da barra lateral aparece assim que publica, as páginas estão agrupadas sob um cabeçalho de categoria (aqui: CUSTOMER PORTAL). O painel de detalhe renderiza breadcrumbs, um botão Edit (canto superior direito), ícones de lápis por campo para edição in-line, separadores adicionais no topo (Details / Vehicles / etc.) e abaixo do formulário uma secção de grelha relacionada que puxa linhas de outra BE filtradas pelo registo atual. O badge do sino ("1") mostra que há uma falha de Event Log não lida. Se algo parecer vazio: a causa mais comum é esquecer-se de virar o interruptor
Published, se estiver OFF, navegar para /companies na barra lateral não mostra
nada. Vire-o para ON e recarregue.
Passo 4 - Limpe um campo ao guardar com um Business Event (5 min)
Os Business Events executam um pouco de lógica assim que um registo é escrito. O mais pequeno
útil: remover os espaços supérfluos do title de um deal a cada gravação, para que
" Acme renewal " fique como "Acme renewal". Vamos montá-lo.
Nunca precisa de uma regra paracreated_at/updated_at/created_by/updated_by. O Archestack adiciona essas quatro colunas de auditoria a cada tabela e carimba-as em cada insert e update automaticamente, por isso um trigger de "carimbar updated_at" só duplicaria trabalho que a plataforma já faz.
- Abra Business Events → Create.
- Rule Name:
Normalize deal.title. Ative Enabled. - Business Entity:
deal. Triggers: marque Before Update. - Sem condições, dispara em cada update.
-
Adicione uma ação: escolha Execute Script. O corpo do script da ação:
string title = Entity.title; Entity.title = title?.Trim(); -
Clique no separador Simulate → escolha qualquer Deal existente → clique em
Run Simulation. O painel de output mostra que o script correu com sucesso e
a que
Entity.titleficou definido. (Não acontece nenhuma escrita real, a simulação corre numa transação revertida.) - Guarde. Teste editando um Deal cujo título tenha espaços no início ou no fim, o título deverá voltar sem espaços a cada gravação.
Porquê Before Update + Execute Script em vez de uma ação que "define
um campo"? O Archestack não tem uma ação discreta "Set field". A forma de mutar um
registo a partir de um trigger é atribuir a Entity.column_name dentro de um
Execute Script num timing Before*. A plataforma persiste o Entity modificado como
parte da escrita em curso, sem query extra, sem risco de recursão.
Passo 5 - Recalcular company.open_deal_value com um Script Module (10 min)
Um total calculado como "valor de deals em aberto por empresa" é demasiado dinâmico para uma coluna armazenada ficar correta à mão. Vamos mantê-lo em sincronização com um pequeno script que volta a correr sempre que algum deal de uma empresa é inserido, atualizado ou apagado.
Escrever o script
Abra Script Modules → Create. Nomeie-o
RecalcCompanyOpenDealValue.
Mude para o separador Parameters. Clique em Add. Name
company_id, Type int, Required marcado.
De volta ao separador Edit. Corpo:
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;
Mude para o separador Test. Escreva um company_id real da sua
página Companies no input de parâmetro, clique em Run. O output mostra o valor
de retorno (o total) e um indicador de sucesso. Recarregue a página Companies,
open_deal_value nessa empresa está agora em sincronização.
Tropeções comuns:
- Esquecer
awaitem.ToListAsync(), o script compila masopenDealsacaba a guardar umTask, não as linhas. O erro no painel Test mencionará "cannot be enumerated".- Usar
Db.Queryem vez deDb.From, não existe um métodoQuery. Cinja-se aDb.From(table)para queries encadeadas eDb.GetAsync(table, id)para registo único por PK.- Tentar
.SumAsync(...), não existe. Vá buscar + some em C#, como acima.
Ligá-lo a um Business Event
- Abra Business Events → Create.
- Rule Name:
Recalc company.open_deal_value on deal change. Enabled ligado. - Business Entity:
deal. Triggers: marque After Create, After Update e Before Delete. - Sem condições, dispara em cada alteração.
-
Adicione uma ação: Execute Script. Corpo:
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 }); - Guarde. Edite o
amountde um Deal e recarregue a página Company, o total mantém-se em sincronização.
Porquê chamá-lo através de Modules em vez de pôr a lógica inline no script deste trigger? Duas razões. Primeiro, o recálculo é reutilizável, também o pode chamar a partir de um Scheduled Event (catch-up noturno) ou diretamente do front-end. Segundo, isola a lógica num único sítio nomeado, mais fácil de testar, mais fácil de encontrar depois.
Passo 6 - Agrupar como package (5 min)
Construiu um mini-CRM real e funcional. Agrupe-o para o poder mover para outro ambiente.
- Abra Packages → Create. Nomeie-o
MiniCRM v1. - Adicione as três Pages. Abra o painel de linked-items para ver a cascata: o package vai trazer as BEs que as páginas referenciam, as tabelas de origem que essas BEs leem, mais os dois Business Events e o Script Module que lhes tocam.
- Desmarque o que preferir omitir, tipicamente manteria tudo para um tutorial como este.
- Clique em Export → faça download do ZIP.
Importar o ZIP num ambiente diferente recria tudo (assumindo que o schema é deployado primeiro). É assim que os parceiros enviam configurações específicas de vertical e como promoveria trabalho de um trial para um ambiente pago mais tarde.
Recapitulação - o que acaba de acontecer
- Desenhou um schema, fez-lhe deploy como tabelas PostgreSQL reais, e nunca escreveu SQL à mão.
- Expôs essas tabelas através de Business Entities, escolhendo o que expor, juntando etiquetas para usabilidade, agregando contagens de linhas relacionadas.
- Compôs três páginas só a partir de configuração, com separadores embutidos e pesquisa.
- Adicionou um comportamento com um Business Event (auto-stamping) e um cálculo mais complexo com um Script Module, oito linhas de C# que correm sempre que os dados mudam.
- Agrupou o conjunto todo como um Package, portável entre ambientes.
O mesmo ciclo escala para dezenas de tabelas e centenas de páginas. A Referência cobre o resto da plataforma, branding, sincronização de dados de terceiros, scaffolding assistido por IA, mas a competência central é aquilo que acabou de aprender.
Para onde ir a seguir
- Gerar faturas em PDF - adicione um template de fatura imprimível aos Deals que acabou de construir. Mesmos dados, novo canal de saída.
- Scheduled Events - estenda o script de recálculo com um catch-up noturno que corre sem ação do utilizador.
- AI Assistant - peça-lhe "suporte para marcações de serviço, data, cliente, veículo, estado" e veja-o gerar uma funcionalidade semelhante.