La lista de invitados que llega desactualizada: dentro de FestCheck, un SaaS de operación de fiestas
Producto propio, en línea: la hoja impresa que llega desactualizada se convierte en conteo, portería y estacionamiento en tiempo real. Por dentro: la regla de cobro como columna generada en Postgres, dos trampas de RLS ocultas por claves foráneas y políticas de rol, y por qué ninguna etiqueta de medición entra en la página de invitación.
Este é um produto próprio da Crazy Diamond, no ar em festcheck.com.br. Não é um sistema entregue a cliente: foi construído por nós, para vender por assinatura a casas de festa. Publicamos aqui pela arquitetura, que resolve três problemas que quase todo SaaS B2B enfrenta — isolamento entre clientes, regra de negócio que não pode divergir, e dado pessoal de quem nunca contratou nada.
O problema começou numa festa de aniversário
A lista de convidados chegou dois dias antes do evento, impressa, e já estava velha: gente havia confirmado depois. No dia, a recepção conferia nome por nome numa folha; o manobrista não sabia quantos carros esperar; e a contagem de quem realmente apareceu só existia na memória de quem estava lá. A conversa com a equipe da casa confirmou que aquilo não era acidente daquela festa — era o processo.
O FestCheck é a ferramenta que substitui essa folha. Convite digital que a família personaliza, confirmação de presença que o convidado responde pelo link, contagem que se atualiza sozinha, controle de vaga de estacionamento e um modo portaria com check-in por busca ou QR. Vendido por assinatura à casa de festas, não à família.
Três contextos, não dois
Quase todo sistema é pensado como desktop e mobile. Este tem três públicos com necessidades incompatíveis. O convite é mobile-first literal: abre no WhatsApp, em telas de 360 px, com alvos de toque de 44 px. O painel é desktop-first literal: tabela é tabela, porque quem administra a casa trabalha sentado, comparando números. E a portaria é uma tela própria — fundo claro para dar contraste sob o sol, alvos de 56 px porque a pessoa opera de pé segurando prancheta, e uma tarefa só na tela.
A regra que decide quanto a casa cobra mora no banco
Casa de festa cobra por convidado, e quase toda casa não cobra criança abaixo de certa idade. Essa régua é dinheiro: define quem entra na conta. Se ela viver no código da aplicação, o painel, a lista impressa e o documento de cobrança podem discordar entre si — e discordar de um cliente sobre a própria fatura é o pior lugar para se estar.
Por isso a regra é uma coluna gerada no PostgreSQL, com uma definição só. E o corte de idade é congelado no evento no momento da criação: se a casa mudar a política amanhã, a festa do mês passado não muda de valor retroativamente.
Isolamento: duas armadilhas que o RLS sozinho não pega
Cada casa de festas é um inquilino, isolado por Row Level Security no Postgres, com FORCE ROW LEVEL SECURITY e a aplicação conectando por um papel sem privilégio — nunca como dona da tabela, porque dono ignora RLS e testar assim não prova isolamento nenhum. Um teste automatizado bloqueia a publicação se o isolamento quebrar.
Duas coisas o RLS não resolve sozinho, e as duas apareceram aqui. A primeira: chave estrangeira não aplica RLS. A verificação de FK é feita pelo sistema, por fora das policies — uma casa não conseguia ler o tema exclusivo de outra e, mesmo assim, conseguia apontá-lo no próprio evento. Toda FK que cruza a fronteira de inquilino precisou de gatilho próprio.
A segunda apareceu num QA de permissões: a policy de edição do perfil da casa perguntava qual casa, e não quem. Resultado: a recepção, que deveria só ler a lista do dia e marcar check-in, conseguia alterar o nome da casa e a régua de cortesia. A trava existia na aplicação; faltava no banco. Proteção que mora só na aplicação vale enquanto ninguém escrever a próxima rotina esquecendo a linha.
Dado de criança, e a medição que não entra no convite
O sistema trata dado pessoal de gente que nunca contratou nada: os convidados, e entre eles crianças. A casa é a controladora, nós somos operadora, e isso está escrito no contrato porque define quem responde perante a ANPD. A idade é opcional e serve a uma finalidade só — aplicar a cortesia. Não há perfilamento nem comunicação de marketing.
A consequência mais visível dessa postura está na medição de audiência. Google Analytics roda apenas nas páginas institucionais. Nas telas de convite, confirmação, passe, painel e portaria não existe tag nenhuma — nem com consentimento dado. Um convite carrega o nome de uma criança, a idade, o dia, a hora e o endereço de onde ela vai estar num sábado; mandar esse caminho para o Analytics seria transformar a lista de convidados de um cliente em evento de marketing nosso. A regra é uma allowlist no código, guardada por teste automatizado: rota nova nasce sem rastreio.
O que está no ar
Next.js 16 com App Router e TypeScript, PostgreSQL no Neon com RLS por inquilino, Drizzle, Clerk para autenticação em domínio próprio, e Vercel. Seis papéis de acesso, do administrador da plataforma ao convidado que edita a própria resposta sem criar conta. Expurgo automático de dados de contato depois da festa. E uma demonstração pública, sem login, que se restaura sozinha todo dia.
283 verificações automatizadas rodam contra o banco real — não contra mock — e cobrem isolamento entre casas, a matriz de permissões dos seis papéis, a regra de cortesia, o encerramento da festa e a allowlist de medição.