Modelo de Dados¶
Versão: 1.0.0 | Data: 2026-03-21 | Status: ✅ Aprovado
Banco de dados: PostgreSQL via Supabase.
A autenticação é gerenciada pelo Supabase Auth — a tabela users é um perfil que estende auth.users.
Diagrama ER¶
erDiagram
users {
uuid id PK
text email
text name
text avatar_url
enum level
enum plan
int daily_interactions_used
timestamp daily_reset_at
timestamp created_at
timestamp updated_at
}
subscriptions {
uuid id PK
uuid user_id FK
text stripe_customer_id
text stripe_subscription_id
enum plan_type
enum status
timestamp current_period_start
timestamp current_period_end
timestamp created_at
}
scenarios {
uuid id PK
text name
text description
text ai_role
enum category
enum difficulty
boolean is_free
boolean is_active
timestamp created_at
}
sessions {
uuid id PK
uuid user_id FK
uuid scenario_id FK
enum type
enum pillar
timestamp started_at
timestamp ended_at
int total_messages
decimal error_rate
timestamp created_at
}
messages {
uuid id PK
uuid session_id FK
enum role
text content
text audio_url
timestamp created_at
}
corrections {
uuid id PK
uuid message_id FK
text original_text
text corrected_text
enum error_type
text explanation
timestamp created_at
}
pronunciation_words {
uuid id PK
uuid message_id FK
text word
int position
decimal accuracy_score
text phoneme_feedback
timestamp created_at
}
user_levels {
uuid id PK
uuid user_id FK
enum from_level
enum to_level
enum reason
timestamp changed_at
}
users ||--o{ subscriptions : "tem"
users ||--o{ sessions : "realiza"
users ||--o{ user_levels : "evolui"
scenarios ||--o{ sessions : "usada em"
sessions ||--o{ messages : "contém"
messages ||--o{ corrections : "recebe"
messages ||--o{ pronunciation_words : "avaliada em"
Tabelas¶
users¶
Perfil do usuário. Estende auth.users do Supabase.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | FK para auth.users.id |
email |
text |
✅ | E-mail do usuário |
name |
text |
✅ | Nome de exibição |
avatar_url |
text |
— | URL da foto de perfil |
level |
enum(A2, B1, B2) |
✅ | Nível atual de inglês |
plan |
enum(free, pro) |
✅ | Plano atual, default free |
daily_interactions_used |
int |
✅ | Interações usadas hoje, default 0 |
daily_reset_at |
timestamp |
✅ | Quando o contador foi zerado pela última vez |
created_at |
timestamp |
✅ | Data de cadastro |
updated_at |
timestamp |
✅ | Última atualização do perfil |
subscriptions¶
Assinatura Pro via Stripe. Um usuário pode ter no máximo uma assinatura ativa.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
user_id |
uuid |
✅ | FK → users.id |
stripe_customer_id |
text |
✅ | ID do cliente no Stripe |
stripe_subscription_id |
text |
✅ | ID da assinatura no Stripe |
plan_type |
enum(monthly, annual) |
✅ | Tipo do plano |
status |
enum(active, canceled, past_due, trialing) |
✅ | Status atual |
current_period_start |
timestamp |
✅ | Início do período atual |
current_period_end |
timestamp |
✅ | Fim do período atual (data de renovação) |
created_at |
timestamp |
✅ | Data da primeira assinatura |
scenarios¶
Cenários de roleplay disponíveis na plataforma.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
name |
text |
✅ | Nome do cenário (ex: "Check-in em hotel") |
description |
text |
✅ | Descrição exibida ao usuário |
ai_role |
text |
✅ | Papel que a IA assume (ex: "Hotel receptionist") |
category |
enum(work, travel, daily) |
✅ | Categoria do cenário |
difficulty |
enum(A2, B1, B2) |
✅ | Nível de dificuldade |
is_free |
boolean |
✅ | Disponível no plano Free, default false |
is_active |
boolean |
✅ | Visível para os usuários, default true |
created_at |
timestamp |
✅ | Data de criação |
Dados iniciais (seed):
| name | category | difficulty | is_free |
|---|---|---|---|
| Check-in em hotel | travel | B1 | true |
| Daily standup em inglês | work | B1 | true |
| Small talk casual | daily | A2 | false |
sessions¶
Cada sessão de conversa iniciada pelo usuário.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
user_id |
uuid |
✅ | FK → users.id |
scenario_id |
uuid |
— | FK → scenarios.id (null se conversa livre) |
type |
enum(free_chat, roleplay) |
✅ | Tipo de sessão |
pillar |
enum(writing, speaking, comprehension) |
✅ | Pilar praticado na sessão |
started_at |
timestamp |
✅ | Início da sessão |
ended_at |
timestamp |
— | Fim da sessão (null se em andamento) |
total_messages |
int |
✅ | Total de mensagens do usuário, default 0 |
error_rate |
decimal(5,2) |
— | % de mensagens com erros (calculado ao encerrar) |
created_at |
timestamp |
✅ | Data de criação |
messages¶
Mensagens individuais de uma sessão (usuário e IA).
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
session_id |
uuid |
✅ | FK → sessions.id |
role |
enum(user, assistant) |
✅ | Quem enviou a mensagem |
content |
text |
✅ | Conteúdo em texto (transcrição se for voz) |
audio_url |
text |
— | URL no Supabase Storage (voz do usuário ou TTS da IA) |
created_at |
timestamp |
✅ | Data/hora da mensagem |
corrections¶
Correções gramaticais e de vocabulário geradas pela IA para mensagens do usuário.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
message_id |
uuid |
✅ | FK → messages.id |
original_text |
text |
✅ | Trecho original com erro |
corrected_text |
text |
✅ | Versão correta sugerida |
error_type |
enum(grammar, vocabulary, pronunciation) |
✅ | Tipo do erro |
explanation |
text |
✅ | Explicação didática do erro |
created_at |
timestamp |
✅ | Data de criação |
pronunciation_words¶
Score de pronúncia por palavra, retornado pelo Azure Speech.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
message_id |
uuid |
✅ | FK → messages.id (mensagem de voz do usuário) |
word |
text |
✅ | Palavra avaliada |
position |
int |
✅ | Posição na frase (ordem) |
accuracy_score |
decimal(5,2) |
✅ | Score de 0 a 100 |
phoneme_feedback |
text |
— | Feedback fonético quando score < 70 |
created_at |
timestamp |
✅ | Data de criação |
user_levels¶
Histórico de evolução de nível do usuário.
| Coluna | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id |
uuid |
✅ | PK |
user_id |
uuid |
✅ | FK → users.id |
from_level |
enum(A2, B1, B2) |
✅ | Nível anterior |
to_level |
enum(A2, B1, B2) |
✅ | Novo nível |
reason |
enum(auto, manual) |
✅ | Origem da mudança |
changed_at |
timestamp |
✅ | Data da mudança |
Regras de Negócio no Banco¶
| Regra | Implementação |
|---|---|
| Limite diário Free (10 interações) | daily_interactions_used + daily_reset_at em users; checado no backend a cada requisição |
| Reset diário do contador | Backend verifica se daily_reset_at < hoje e zera daily_interactions_used |
| Plano do usuário | Campo plan em users atualizado via webhook do Stripe |
| Histórico de nível | Toda mudança em users.level gera um registro em user_levels via trigger |
| error_rate da sessão | Calculado e salvo em sessions.error_rate ao encerrar a sessão |
Índices¶
| Tabela | Coluna(s) | Motivo |
|---|---|---|
sessions |
user_id, started_at DESC |
Listar histórico do usuário ordenado por data |
messages |
session_id, created_at ASC |
Carregar conversa em ordem cronológica |
corrections |
message_id |
Buscar correções de uma mensagem |
pronunciation_words |
message_id, position ASC |
Buscar palavras de uma mensagem em ordem |
user_levels |
user_id, changed_at DESC |
Histórico de evolução do usuário |