KOLOS — Single User, Multiple Contexts (Multi-atores)
🧭 Contexto
O KOLOS tem “atores” (papéis) e o mesmo usuário pode exercer múltiplos deles.
Atores suportados no App (Flux):
| Ícone |
Ator |
Role |
UI |
Descrição |
| 🏟️ |
Arena Owner |
venue_owner |
App Custom (Flux) |
Dono da arena: gestão de quadras, operação e negócio local. |
| 🏆 |
Organizer |
organizer |
App Custom (Flux) |
Produtor de torneios: mesa de controle, chaves e horários. |
| 🎓 |
Coach |
coach |
App Custom (Flux) |
Professor: aulas, turmas e evolução técnica. |
| 🛡️ |
Staff |
staff |
App Custom (Flux) |
Equipe da arena: operação (gatekeeper/check-in etc.). |
| 🏃 |
Athlete |
player |
App Custom (Flux) |
Usuário final: “play”, reservas, inscrições e stats. |
Super Admin fica isolado:
| Ícone |
Ator |
Role |
UI |
Regra |
| 👑 |
Super Admin |
super_admin |
Filament (/admin) |
Isolado do app Flux; acesso global apenas no painel. |
A pergunta arquitetural é: um único login deve suportar múltiplos papéis (multi-role), e como fazemos isso sem:
- confundir UX ("onde estou?")
- criar brechas de segurança ("escape" de permissões)
- acoplar tudo a middleware de role de forma rígida
Nossa stack já favorece isso:
- Spatie Permission para roles/permissões.
- Policies para escopo por recurso (ex.: dono da arena).
- Livewire 4 + Flux UI no app principal.
- Filament isolado para Super Admin.
✅ Avaliação de estratégias (prós/cons)
1) Contas separadas por ator (multi-account)
- ✅ Simplicidade mental (cada conta tem um papel)
- ✅ Segurança por isolamento
- ❌ UX ruim (múltiplos logins, duplicação de dados)
- ❌ Gestão de identidade e suporte vira problema (troca de e-mail/telefone etc.)
Conclusão: útil apenas como fallback, não como padrão.
2) Um usuário com múltiplas roles ativas (multi-role sem “modo”)
- ✅ Menos complexidade de estado (não existe “contexto ativo”)
- ✅ Navegação pode mostrar tudo que o usuário pode acessar
- ❌ UX pode ficar confusa (“quais dados estou vendo?”)
- ❌ Fácil cair em N+1 de regras na UI e esquecer de aplicar Policy
Conclusão: bom para segurança (se Policies forem a fonte da verdade), mas pode degradar UX conforme crescem features.
3) Um usuário com múltiplas roles + “Contexto Ativo” (switcher)
Exemplos de produto (analogia): Facebook/Instagram (perfil vs página), GitHub (pessoal vs organização), Google Workspace (contas/organizações).
- ✅ UX clara: usuário escolhe “como está operando agora”
- ✅ UI fica mais simples (navegação e defaults mudam)
- ✅ Prepara bem “escopo de arena” (venue selecionada)
- ⚠️ Requer disciplina: contexto ativo não pode ser usado como segurança
Conclusão: melhor estratégia para KOLOS.
🏁 Decisão
Adotar Single User + Multiple Roles, com Contexto Ativo apenas para UX.
Regras de ouro:
- Segurança sempre por Policy/Permission, nunca por contexto de sessão.
- Contexto ativo define:
- o “modo” do app (Player / Venue / Coach …)
- o “escopo” padrão (ex.: venue selecionada)
- a navegação, landing pages e filtros default
- O mundo Super Admin continua isolado no Filament (
/admin).
🔐 Modelo de autorização (fonte da verdade)
- Permissions: capacidade geral (ex.:
venue.manage_courts, venue.declare_maintenance).
- Policies: escopo por recurso (ex.:
VenuePolicy::declareMaintenance(User, Venue) valida dono/escopo).
- Middlewares de role podem existir para “portas grossas” (ex.: esconder rotas do mundo Player), mas não substituem Policies.
🧠 O que é “Contexto Ativo” (e o que não é)
Contexto ativo é um estado leve (em sessão) para UX:
active_actor: enum/string (ex.: player, venue_owner, coach)
active_venue_id: opcional, quando o ator depende de escopo de arena
O contexto ativo não pode:
- conceder acesso a rotas
- permitir escrita sem Policy
- substituir a checagem
can()/authorize()
🧩 Modelagem de escopo (o ponto crítico)
A parte realmente difícil em apps multi-atores não é “ter múltiplas roles”, e sim o escopo:
- Venue Owner tem escopo claro: é dono (
venues.owner_user_id).
- Staff/Coach precisa de vínculo explícito com arena.
Recomendação (quando chegarmos nisso): criar uma tabela de vínculo (exemplo):
venue_memberships
id
venue_id
user_id
role (ex.: staff, coach)
status (active/inactive)
- timestamps
Assim, Policies conseguem validar:
- “usuário é staff desta arena?”
- “usuário é coach desta arena?”
Sem isso, qualquer liberação para staff/coach vira gambiarra.
🖥️ UX recomendada (Livewire/Flux)
- No
x-app-shell, adicionar um Profile Switcher (ex.: menu do avatar):
- mostra papéis disponíveis ao usuário
- troca
active_actor
- quando aplicável, pede/define
active_venue_id
- Landing page pós-login:
- se só existe 1 ator → vai direto
- se existem vários → sugere seleção (sem bloquear acesso, apenas UX)
Importante: se o usuário trocar para “Venue Owner”, a UI deve guiar para “Minhas arenas” e permitir escolher a arena (escopo).
🛠️ Diretrizes de implementação (quando formos codar)
- Criar um serviço de contexto (ex.:
App\Support\ActorContext) que:
- lê/grava
session('actor.active_actor')
- lê/grava
session('actor.active_venue_id')
- valida consistência (ex.: venue selecionada pertence ao usuário)
- Middleware opcional “UX-only” para garantir que o contexto está setado antes de certas telas (redireciona para seleção).
- Livewire:
- preferir
#[Locked] para IDs derivados do contexto quando necessário.
- nunca confiar em IDs vindos do client sem Policy.
🧪 Validação
- Testes de Policy continuam sendo a garantia real.
- Feature tests garantem:
- usuário multi-role acessa as áreas que pode
- troca de contexto muda navegação/landing, mas não “cria permissão”
📦 Impacto
- Impacto imediato: documentação + direção arquitetural.
- Impacto futuro: criação do switcher e do modelo de membership para staff/coach.