O contexto
Marketplace de serviços domésticos: eletricista, encanador, técnico de refrigeração. De um lado o cliente com um problema em casa; do outro, o profissional autônomo que precisa de trabalho.
O ciclo real não é "pedir e receber". É: cliente solicita, profissional aceita, marcam visita técnica, o profissional vai até o local, mede o serviço, manda orçamento, o cliente aprova ou recusa, o serviço é executado, o cliente avalia. Cada uma dessas etapas pode travar, voltar ou ser abandonada.
O problema
Marketplace de serviço morre pelo estado do pedido. Se o cliente pode aprovar um orçamento que ainda não foi enviado, ou avaliar um serviço que não foi executado, ou se o profissional consegue mandar orçamento antes de ter feito a visita, o sistema perde a noção de onde cada pedido está — e a operação passa a resolver isso por telefone.
O erro comum é espalhar esse controle por booleanos: visita_confirmada, tem_orcamento, foi_pago, avaliado. Com quatro flags existem dezesseis combinações, e a maioria delas é inválida. Ninguém escreve a regra que impede as combinações inválidas, e elas acontecem.
Havia ainda dois públicos com necessidades opostas na administração: a operação da plataforma, que precisa ver tudo, e o profissional autônomo, que precisa ver só o que é dele.
Decisões técnicas
Máquina de estados explícita em vez de flags booleanas. O pedido tem um estado, um só: aguardando_confirmacao → visita_confirmada → aguardando_orcamento → orcamento_recebido → servico_em_andamento → concluido → avaliado. As transições permitidas são declaradas, e qualquer tentativa fora delas é rejeitada no backend. Isso custa mais código no começo — é mais fácil escrever $pedido->avaliado = true. Paga depois: estado inválido deixa de ser possível, e o frontend não precisa reimplementar a regra para decidir qual botão mostrar. Ele pergunta o estado e renderiza o que aquele estado permite.
A máquina de estados mora no backend, não no frontend. O Next.js podia calcular a próxima etapa e a tela ficaria mais reativa. Mas regra que vive no cliente é regra que se contorna com o DevTools aberto, e teria que ser duplicada em qualquer app futuro. Backend decide, frontend exibe.
Dois painéis Filament separados em vez de um painel com permissões. /gestao para a operação, /painel para o profissional. A alternativa seria um painel único com controle de acesso por papel, o que é menos código e um risco maior: cada tela nova precisa lembrar de esconder o que o profissional não pode ver, e o dia em que alguém esquece é um vazamento entre prestadores. Painéis separados fazem do isolamento a configuração padrão, não uma checagem que precisa ser lembrada.
Filament em vez de painel administrativo à mão. Os dois painéis são CRUD sobre os mesmos models já usados pela API. Escrevê-los manualmente seriam semanas de formulário e tabela sem diferencial competitivo nenhum. O tempo foi para a máquina de estados e o fluxo de orçamento, que é o produto.
Frontend mobile-first, não app nativo. O cliente que precisa de encanador não instala aplicativo antes. Web mobile-first em Next.js 16 remove a barreira de instalação; app pode vir depois, consumindo a mesma API.
Sanctum. O frontend é primeiro-parte contra a própria API — token simples resolve, OAuth2 seria cerimônia sem terceiro consumindo.
O que ficou pronto
Marketplace com solicitação, agendamento de visita técnica, envio e aprovação de orçamento, execução e avaliação, com o ciclo inteiro governado pela máquina de estados. Painel de gestão e painel do profissional em Filament 3, API REST autenticada com Sanctum, deploy em Docker.