20 KiB
Subdomain Stores Plan
Objetivo
Reorganizar o ecossistema do PediFoods para um modelo claro de subdomínios, mantendo a integração com o backend do Atomenta onde ela faz sentido, mas separando melhor identidade, navegação e contexto de cada área.
Cenário Desejado
1. pedifoods.com.br
Papel:
- home pública
- landing page
- login
- onboarding inicial
- após login, exibir lista de lojas do usuário
Responsabilidade:
- identidade pública do PediFoods
- autenticação do usuário final/parceiro
- seleção da loja
2. parceiro.pedifoods.com.br
Papel:
- painel do lojista
- equivalente ao atual "Atomenta Store"
- gestão operacional da loja
Responsabilidade:
- backoffice do parceiro
- configuração da loja
- cardápio
- pedidos
- financeiro
- onboarding do parceiro
3. nome-curto-loja.pedifoods.com.br
Papel:
- storefront da loja
- vitrine pública ou semi-logada da loja
- produtos, categorias, carrinho e experiência do cliente
Responsabilidade:
- experiência da loja individual
- branding e contexto por loja
- leitura do catálogo
Princípio Arquitetural
O PediFoods não deve mais parecer "um Atomenta renomeado".
Ele pode continuar:
- consumindo APIs do Atomenta
- reutilizando lógica de OTP
- reutilizando cookies/tokens e integração segura quando necessário
Mas deve deixar claro:
- qual domínio é PediFoods
- qual domínio é painel parceiro
- qual domínio é storefront da loja
Direção Técnica
Separar 3 contextos
public app
- domínio base
pedifoods.com.br - homepage, login, seleção de lojas
partner app
- subdomínio fixo
parceiro.pedifoods.com.br - painel do lojista
store app
- subdomínio dinâmico
{slug}.pedifoods.com.br - storefront da loja
Manter 1 base de código no curto prazo
No curto prazo, o melhor custo-benefício é manter uma única codebase do PediFoods com roteamento por host.
Ou seja:
- o app lê o
Host - identifica o contexto
- renderiza a área correta
Exemplo de resolução:
pedifoods.com.br=> contextopublicparceiro.pedifoods.com.br=> contextopartnerslug.pedifoods.com.br=> contextostore
Isso evita:
- duplicação de projeto
- deploy múltiplo desnecessário
- divergência de regra de negócio
Regras de Resolução de Host
Contexto public
Hosts:
pedifoods.com.brwww.pedifoods.com.brse existir
Fluxos:
- home
- login
- cadastro
- recuperação de conta
- listagem de lojas do usuário autenticado
Contexto partner
Host:
parceiro.pedifoods.com.br
Fluxos:
- login do parceiro
- dashboard da loja
- onboarding
- catálogo
- pedidos
- financeiro
Contexto store
Host:
{storeSlug}.pedifoods.com.br
Fluxos:
- catálogo público da loja
- detalhes da loja
- carrinho
- pedidos do cliente
Mudanças Estruturais Necessárias
1. Resolver host de forma centralizada
Criar uma camada única para:
- ler
req.hostname - classificar o contexto
- extrair
storeSlugquando aplicável
Saída esperada:
appContext.type = public | partner | storeappContext.storeSlug = string | null
2. Parar de usar defaults de domínio do Atomenta no PediFoods
Hoje o PediFoods ainda carrega vários defaults como:
https://atomenta.com.bratomenta_tokenatomenta_auth- nomes e textos herdados
Isso precisa ser separado em 2 grupos:
Pode continuar apontando para Atomenta
- integração de API interna
- OTP/crypto
- comunicação backend-to-backend
- consulta de dados mestres, se esta for a arquitetura
Não deve mais apontar para Atomenta
- branding
- domínio público
- base URL de navegação do usuário
- nomes do app
package.jsoneecosystem.config.js
3. Definir estratégia de sessão/cookies
Decisão importante:
O sistema terá dois contextos de autenticação diferentes:
- partner session
- usada no
parceiro.pedifoods.com.br - representa o lojista/operador
- dá acesso ao painel administrativo
- customer session
- usada em
pedifoods.com.bre em{slug}.pedifoods.com.br - representa o cliente/comprador
- dá acesso à navegação, seleção de lojas, carrinho e compra
Essas duas sessões não devem ser compartilhadas entre si.
Se fossem compartilhadas:
- um usuário logado como parceiro poderia cair autenticado na storefront da própria loja
- haveria risco de misturar permissões administrativas com experiência de compra
- o modelo de segurança e UX ficaria ambíguo
Opção A
Sessão única compartilhada em .pedifoods.com.br
Resultado:
- mesma sessão valeria para:
pedifoods.com.brparceiro.pedifoods.com.br{slug}.pedifoods.com.br
Problema:
- não serve para este cenário
- mistura sessão de lojista com sessão de cliente
Opção B
Sessões separadas por contexto
Modelo recomendado:
parceiro.pedifoods.com.br- cookie próprio de parceiro
- escopo exclusivo do painel
pedifoods.com.br- cookie próprio de cliente
- pode ser compartilhado com
{slug}.pedifoods.com.brse fizer sentido para a jornada do comprador
{slug}.pedifoods.com.br- usa sessão do cliente, nunca sessão do parceiro
Vantagens:
- separa claramente os perfis
- evita vazamento de autenticação entre painel e loja
- reduz risco de autorização indevida
- deixa a jornada de compra independente da jornada administrativa
Desvantagens:
- exige mais cuidado no desenho de login/logout
- exige nomes de cookies e middleware distintos
Recomendação:
- usar sessões separadas por contexto
- nunca compartilhar sessão de
partnercomcustomer - se necessário, compartilhar apenas a sessão do cliente entre:
pedifoods.com.br{slug}.pedifoods.com.br
Implementação sugerida:
- cookie de parceiro:
- exemplo
pedifoods_partner_session - domínio restrito a
parceiro.pedifoods.com.br
- exemplo
- cookie de cliente:
- exemplo
pedifoods_customer_session - domínio
.pedifoods.com.brapenas se a jornada do cliente precisar navegar entre domínio principal e subdomínios de loja
- exemplo
Estratégia de Dados
Fonte de verdade
Precisamos assumir explicitamente:
- o Atomenta continua sendo a fonte de verdade operacional
- o PediFoods atua como camada especializada de experiência/lojista/storefront
Isso significa:
- login e sessão podem existir no PediFoods
- mas dados de loja, catálogo, usuário parceiro e regras sensíveis podem continuar vindo do Atomenta via API
Fases de Implementação
Fase 1. Higiene e identidade
Objetivo:
- remover confusão estrutural atual
Itens:
- corrigir
package.jsondo PediFoods - corrigir
ecosystem.config.js - revisar
env.example - revisar nomes, textos e defaults públicos
- separar claramente o que é branding PediFoods e o que é integração Atomenta
Saída:
- o projeto deixa de se identificar como Atomenta
Fase 2. Resolver host e contexto
Objetivo:
- introduzir leitura de subdomínio
Itens:
- criar utilitário central de hostname
- classificar
public,partner,store - disponibilizar contexto em middleware
- ajustar templates/layouts e rotas para reagirem ao contexto
Saída:
- uma única app responde diferente por host
Fase 3. Fluxo público pedifoods.com.br
Objetivo:
- consolidar homepage + login + listagem de lojas
Itens:
- manter home atual
- após login, mostrar lojas do usuário
- ao clicar na loja, redirecionar para
slug.pedifoods.com.br
Saída:
- domínio principal vira hub do usuário
Fase 4. Fluxo parceiro parceiro.pedifoods.com.br
Objetivo:
- isolar o backoffice do lojista
Itens:
- mover/ajustar as telas que hoje se comportam como "Atomenta Store"
- garantir que esse host sempre abra painel parceiro
- revisar permissões e middleware
Saída:
- painel do lojista com identidade própria
Fase 5. Fluxo loja slug.pedifoods.com.br
Objetivo:
- transformar storefront em domínio por loja
Itens:
- mapear slug -> loja
- carregar tema, catálogo e dados por slug
- revisar SEO/meta tags
- revisar links absolutos
Saída:
- cada loja com seu subdomínio
Fase 6. Sessão compartilhada
Objetivo:
- permitir navegação fluida entre subdomínios
Itens:
- cookie domain
.pedifoods.com.br - revisar login/logout
- revisar redirecionamentos
- revisar callbacks OTP
Saída:
- usuário loga uma vez e navega entre contextos compatíveis
Riscos
1. Misturar branding com integração
Se trocar tudo de atomenta cegamente, pode quebrar integrações reais.
2. Cookies atuais
Se hoje os cookies ainda usam nomes herdados, uma troca brusca pode invalidar sessões e OTP.
3. Links absolutos hardcoded
Há muitos pontos do PediFoods ainda apontando para Atomenta. Eles precisam ser classificados antes de alterar.
4. Deploy atual
Hoje o deploy sobe um projeto que ainda está semanticamente misturado. Sem limpeza mínima, a arquitetura nova vai nascer confusa.
Recomendações Práticas
Curto prazo
- corrigir identidade mínima do projeto PediFoods
- mapear todos os usos de:
atomenta.com.bratomenta_tokenatomenta_authATOMENTA_*
- classificar cada ocorrência em:
- branding
- integração legítima
Médio prazo
- implementar middleware de contexto por host
- separar layouts
public,partner,store - começar pelo domínio
parceiro.pedifoods.com.br
Longo prazo
- consolidar storefront por slug
- revisar autenticação multi-subdomínio
- avaliar se no futuro compensa separar em múltiplos apps
Ordem Recomendada de Execução
- Higienizar identidade do projeto PediFoods
- Mapear referências herdadas de Atomenta
- Criar resolvedor central de host/subdomínio
- Subir
parceiro.pedifoods.com.br - Ajustar
pedifoods.com.brpara login + lista de lojas - Implementar
slug.pedifoods.com.br - Revisar cookies e sessão compartilhada
Decisão Recomendada Agora
A melhor decisão agora é:
- não tentar “desatomentar” tudo de uma vez
- primeiro separar:
- identidade pública
- painel parceiro
- storefront por subdomínio
- e só depois revisar dependências herdadas uma a uma
Próximo Passo
Próxima entrega recomendada:
- fazer uma auditoria do repositório PediFoods
- classificar referências herdadas em:
pode trocar agoradepende de integração com Atomenta
- implementar o resolvedor de subdomínio
- subir primeiro o contexto
parceiro.pedifoods.com.br
Tasks de Implementação
Legenda:
- 🔴 pendente
- 🟢 concluído
Fase 1. Higiene do projeto PediFoods
- 🟢 Corrigir identidade básica do projeto em
package.json - 🟢 Corrigir identidade básica do processo em
ecosystem.config.js - 🔴 Revisar
env.examplepara separar branding PediFoods de integração Atomenta - 🟢 Mapear todas as referências a
atomentano repositório - 🟢 Classificar cada referência como:
- branding/UI
- integração técnica legítima
- legado morto
- 🟢 Remover ou ajustar branding incorreto do PediFoods
Fase 2. Contexto de domínio e subdomínio
- 🟢 Criar utilitário central para resolver o host atual
- 🟢 Criar classificador de contexto:
publicpartnerstore
- 🟢 Extrair
storeSlugautomaticamente quando o host for{slug}.pedifoods.com.br - 🟢 Injetar esse contexto em middleware global
- 🟢 Expor o contexto para views/templates
- 🔴 Garantir fallback seguro para domínio desconhecido
Fase 3. Sessões separadas
- 🟢 Criar sessão de parceiro independente
- 🔴 Criar sessão de cliente independente
- 🔴 Garantir que
parceiro.pedifoods.com.brnunca reutilize sessão de cliente - 🔴 Garantir que
{slug}.pedifoods.com.brnunca reutilize sessão de parceiro - 🟢 Revisar nomes dos cookies
- 🟢 Revisar
login,logoute middleware de autenticação - 🟢 Revisar redirecionamentos pós-login por contexto
Fase 4. pedifoods.com.br
- 🟢 Consolidar a home pública
- 🟢 Consolidar login/cadastro/recuperação
- 🔴 Criar tela pós-login com listagem de lojas do usuário
- 🟢 Fazer o clique na loja redirecionar para
{slug}.pedifoods.com.br - 🟢 Revisar meta tags, canonical e links absolutos
Fase 5. parceiro.pedifoods.com.br
- 🟢 Isolar o painel do lojista nesse host
- 🟢 Revisar middleware e permissões do painel
- 🟢 Revisar links internos do painel para não mandar ao domínio errado
- 🟢 Ajustar onboarding do parceiro
- 🟢 Ajustar rotas administrativas de loja, pedidos, catálogo e financeiro
- 🟢 Garantir que o painel não renderize a experiência pública da loja
Fase 6. {slug}.pedifoods.com.br
- 🟢 Criar resolução da loja por slug
- 🟢 Buscar loja e catálogo pelo slug
- 🟢 Renderizar storefront por loja
- 🟢 Ajustar tema, título, descrição e identidade da loja
- 🟢 Ajustar links compartilháveis da loja
- 🟢 Revisar SEO e OpenGraph por loja
- 🔴 Garantir fallback para slug inexistente
Fase 7. Gestão de slug da loja
- 🟢 Adicionar campo
storeSlugcomo atributo persistido da loja - 🟢 Validar unicidade global do slug
- 🟢 Validar formato do slug
- 🟢 Gerar pré-preenchimento automático do slug no fim do onboarding com base no nome fantasia da loja
- 🟢 Criar tela de configuração do slug no painel parceiro
- 🟢 Exibir aviso de impacto da troca de slug
- 🟢 Salvar data da última alteração de slug
- 🟢 Bloquear nova alteração por 90 dias
- 🟢 Exibir contador ou data de desbloqueio para nova alteração
- 🔴 Criar redirect temporário do slug antigo para o novo, se viável
Fase 8. Integração com Atomenta
- 🟢 Revisar quais chamadas devem continuar indo ao Atomenta
- 🟢 Centralizar base URL interna da API do Atomenta
- 🔴 Revisar OTP e autenticação compartilhada
- 🟢 Revisar cookies herdados
atomenta_* - 🔴 Revisar endpoints de catálogo, loja, financeiro e customer lookup
- 🔴 Remover dependências herdadas desnecessárias
Fase 9. Deploy e infraestrutura
- 🔴 Ajustar nginx/proxy para:
pedifoods.com.brparceiro.pedifoods.com.br*.pedifoods.com.br
- 🔴 Validar wildcard localmente antes da etapa de DNS público
- 🔴 Garantir certificado TLS compatível com wildcard
- 🔴 Validar resolução correta de host até a app Node
Status Atual
Legenda:
- 🟢 concluído
- 🟡 parcial
- 🔴 pendente
Resumo do que já foi entregue no app:
- 🟢 identidade básica do projeto corrigida
- 🟢 resolução central de host/subdomínio
- 🟢 separação inicial entre
public,partnerestore - 🟢 slug canônico com preview, unicidade, cooldown de 90 dias e geração automática no onboarding
- 🟢 storefront canônica em
{slug}.pedifoods.com.br - 🟢 links públicos e links do painel abrindo a loja publicada correta
- 🟢 limpeza principal de branding visível do PediFoods
- 🟢 frontend priorizando
pedifoods_partner_tokencom fallback para legado
O que ainda falta para fechar o fluxo completo:
- 🔴 autenticação real do cliente
- 🔴 tela pós-login do cliente com listagem de lojas
- 🔴 fallback para slug inexistente
- 🔴 redirect temporário de slug antigo para slug novo
- 🔴 wildcard real em proxy/Cloudflare/TLS
- 🔴 Validar deploy do PediFoods sem contaminar com Atomenta
- 🔴 Configurar wildcard DNS no Cloudflare
- esta task deve ficar por último
- quando chegarmos nela, você executa manualmente no Cloudflare
- eu só vou te avisar e te passar o valor exato
Fase 10. Testes de ponta a ponta
- 🔴 Testar login de cliente em
pedifoods.com.br - 🔴 Testar seleção de loja e redirecionamento para
{slug}.pedifoods.com.br - 🔴 Testar login de parceiro em
parceiro.pedifoods.com.br - 🔴 Testar isolamento entre sessão de parceiro e sessão de cliente
- 🔴 Testar troca de slug com bloqueio de 90 dias
- 🔴 Testar slug inválido/inexistente
- 🔴 Testar logout em cada contexto
- 🔴 Testar links absolutos, redirects e cookies
Política de Slug da Loja
Sua ideia funciona e faz sentido.
Recomendação
O slug deve ser:
- criado uma primeira vez no onboarding da loja
- editável pelo parceiro no painel
- bloqueado por 90 dias após cada alteração
Motivos para essa regra
- evita troca frequente de URL
- reduz confusão para clientes recorrentes
- protege links já compartilhados
- reduz impacto operacional em cache, indexing e hábito do usuário
Como funcionaria
Cada loja teria campos como:
storeSlugslugUpdatedAtslugLockedUntilslugGeneratedBySystemAtslugFirstManualUpdateAt
Regra:
- se nunca definiu slug:
- o sistema pré-preenche automaticamente
- o usuário pode ajustar antes de concluir
- se já definiu:
- só pode alterar novamente após 90 dias
Geração inicial do slug
Recomendação:
- ao terminar o onboarding, gerar um pré-preenchimento automático baseado no nome fantasia
Exemplo:
Pizzaria do Zé->pizzaria-do-zeTemaki House Prime->temaki-house-prime
O usuário:
- vê o slug sugerido
- pode editar antes de salvar pela primeira vez
Importante:
- o slug gerado automaticamente pelo sistema não inicia a janela de bloqueio de 90 dias
- ele serve apenas como valor inicial/sugestão persistida
- o contador de 90 dias só começa quando houver a primeira alteração manual do lojista
Limite de tamanho
Recomendação:
- limitar o slug a 30 caracteres
Esse limite deve contar:
- letras
- números
- hífens
Na prática:
- espaços viram hífen
- então o total final do slug salvo não pode passar de
30
Exemplo de regra:
- nome fantasia muito grande:
Loja Muito Grande Com Nome Enorme da Silva
- slug gerado:
loja-muito-grande-com-nome
Regra de sanitização sugerida
O gerador automático deve:
- converter para minúsculas
- remover acentos
- trocar espaços por hífen
- remover caracteres especiais
- colapsar hífens duplicados
- cortar em
30caracteres - remover hífen do início/fim
Se o slug já existir
Se o slug gerado já estiver em uso:
- adicionar sufixo incremental curto
Exemplo:
pizzaria-do-zepizzaria-do-ze-2pizzaria-do-ze-3
Sempre respeitando o limite final de 30 caracteres
Regra de bloqueio de 90 dias
Regra correta:
-
geração automática pelo sistema
- não conta como alteração manual
- não bloqueia o campo
-
primeira edição manual feita pelo lojista
- passa a contar como alteração real
- grava
slugFirstManualUpdateAt - grava
slugUpdatedAt - define
slugLockedUntil = slugUpdatedAt + 90 dias
-
edições seguintes
- só permitidas após
slugLockedUntil
- só permitidas após
Exemplo prático
- onboarding termina
- sistema gera:
pizzaria-do-ze
- lojista vê o valor e troca para:
pizza-do-ze-centro
- essa troca manual:
- conta como primeira alteração real
- inicia a trava de 90 dias
Se o lojista aceitar o slug automático sem mexer:
- nenhum bloqueio de 90 dias deve ser iniciado
- ele ainda pode fazer a primeira alteração manual depois
Ajuste das tasks
- 🔴 Persistir metadado informando se o slug atual foi gerado automaticamente ou definido manualmente
- 🔴 Garantir que a geração automática no onboarding não inicie
slugLockedUntil - 🔴 Iniciar a janela de 90 dias somente na primeira alteração manual do lojista
Validações necessárias
- slug único no sistema
- somente letras minúsculas, números e hífen
- tamanho mínimo e máximo
- blacklist de reservados, por exemplo:
wwwapiadminappparceiromailgitproxy
Redirect do slug antigo
Se possível, é recomendável:
- guardar o slug antigo
- fazer redirect
301do slug antigo para o novo por um período
Isso ajuda:
- SEO
- links salvos
- usuários acostumados com a URL antiga
Sobre DNS e Cloudflare
Isso funciona bem se você usar:
- wildcard DNS
*.pedifoods.com.br - proxy reverso lendo o
Host
Nesse modelo:
- você não precisa criar um registro DNS novo por loja
- o wildcard cobre todos os slugs
- a troca de slug depende mais da aplicação e do cache HTTP do que de propagação DNS individual
Ou seja:
- a trava de 90 dias continua fazendo sentido por produto e UX
- mas não por limitação técnica de DNS em si
Recomendação final para slug
Implementar assim:
- slug definido no onboarding
- edição permitida no painel parceiro
- nova alteração somente após 90 dias
- aviso visual claro antes de salvar
- registro do slug antigo para redirect temporário
Autor: Daniel Arantes Loverde