Ir para o conteúdo

Backend - Estrutura do Banco de Dados

Este documento descreve a estrutura do banco de dados PostgreSQL, incluindo tabelas, relacionamentos, índices e constraints.

Índice

  1. Visão Geral
  2. Tabelas Principais
  3. Relacionamentos
  4. Índices
  5. Constraints
  6. Tipos de Dados Especiais

Visão Geral

Banco de Dados: PostgreSQL 12+

ORM: GORM v1.25.5

Driver: pgx/v5

Localização da Configuração: internal/database/database.go

Características

  • Soft Deletes: Maioria das tabelas suporta soft delete (deleted_at)
  • Timestamps: created_at, updated_at automáticos
  • JSONB: Usado para campos complexos (arrays, metadados)
  • DATE: Tipo DATE para datas civis (não TIMESTAMP)

Tabelas Principais

users

Tabela principal de usuários.

Campos Principais: - id - Primary Key - username - Opcional, apenas para exibição - email - Único, usado para login - password - Hash bcrypt - name - Nome completo - user_type - 'client', 'professional', 'admin' - is_active - Se está ativo - email_verified - Se email foi verificado - email_verified_at - Data de verificação

Índices: - email - Unique Index - username - Index (permite NULLs múltiplos)

Relacionamentos: - 1:N com accounts - 1:N com subscriptions - 1:N com cycles - 1:N com observations - 1:1 com client_profiles - 1:1 com professional_profiles


cycles

Ciclos menstruais.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - start_date - DATE (primeiro dia de sangramento) - end_date - DATE (opcional, último dia) - length - Duração em dias - is_active - Se está ativo - sync_id - Para sincronização mobile

Índices: - user_id - Index - sync_id - Index

Constraints: - Apenas um ciclo ativo por usuário (lógica de negócio)

Relacionamentos: - N:1 com users - 1:N com observations - 1:N com cycle_comments


observations

Observações diárias do ciclo.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - cycle_id - Foreign Key → cycles (opcional) - date - DATE (data da observação) - type - Tipo de observação - sensation - Sensação física - symbol_id - Foreign Key → symbol - appearance_id - Foreign Key → appearances - had_intercourse - Teve relação sexual - is_first_day_of_menstruation - Primeiro dia da menstruação - professional_notes - Notas do profissional - sync_id - Para sincronização mobile

Índices: - user_id - Index - cycle_id - Index - symbol_id - Index - appearance_id - Index - sync_id - Index

Constraints: - Apenas uma observação por dia por usuário (lógica de negócio)

Relacionamentos: - N:1 com users - N:1 com cycles - N:1 com symbol - N:1 com appearances


symbol

Símbolos do Método Billings.

Campos Principais: - id - Primary Key - name - Nome único do símbolo - description - Descrição - image_path - Caminho da imagem (R2 ou local) - relation_image_path - Imagem para relação sexual

Índices: - name - Unique Index

Relacionamentos: - 1:N com observations


sensations

Sensações físicas.

Campos Principais: - id - Primary Key - name - Nome único - display_name - Nome para exibição - description - Descrição - is_active - Se está ativa - order - Ordem de exibição

Índices: - name - Unique Index


appearances

Aparências visuais do muco.

Campos Principais: - id - Primary Key - name - Nome único - display_name - Nome para exibição - description - Descrição - is_active - Se está ativa - order - Ordem de exibição

Índices: - name - Unique Index

Relacionamentos: - 1:N com observations


day_based_rules

Regras baseadas em dias do ciclo.

Campos Principais: - id - Primary Key - name - Nome da regra - description - Descrição - min_day - Dia mínimo (inclusive) - max_day - Dia máximo (inclusive) - allowed_symbol_ids - JSONB array de IDs permitidos - restricted_symbol_ids - JSONB array de IDs restritos - is_active - Se está ativa - order - Ordem de aplicação

Tipos Especiais: - allowed_symbol_ids - JSONB (ex: [1, 2, 3]) - restricted_symbol_ids - JSONB (ex: [4, 5])


client_profiles

Perfis detalhados de clientes.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users (Unique) - full_name - Nome completo - birth_date - DATE - objective - Objetivo (engravidar, espaçar, conhecimento) - first_menstruation_date - DATE (menarca) - average_cycle_length - Duração média do ciclo - woman_pattern - Padrão Básico de Infertilidade (PBI) - pbi_sensation_id - Foreign Key → sensations - pbi_appearance_id - Foreign Key → appearances - profile_photo_key - Chave da foto no R2

Índices: - user_id - Unique Index

Constraints: - user_id é único (garante 1:1)

Relacionamentos: - 1:1 com users


professional_profiles

Perfis detalhados de profissionais.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users (Unique) - full_name - Nome completo - status - 'pending', 'approved', 'rejected' - approved_by - ID do admin que aprovou - approved_at - Data de aprovação - rejection_reason - Motivo da rejeição - profile_photo_key - Chave da foto no R2 - professional_certificate_key - Chave do certificado - document_key - Chave do documento

Índices: - user_id - Unique Index

Constraints: - user_id é único (garante 1:1)

Relacionamentos: - 1:1 com users


professional_patients

Vínculos entre profissionais e pacientes.

Campos Principais: - id - Primary Key - professional_id - Foreign Key → users - patient_id - Foreign Key → users (Unique) - status - 'pending', 'approved', 'rejected'

Índices: - professional_id - Index - patient_id - Unique Index

Constraints: - patient_id é único (garante 1 profissional por paciente)

Relacionamentos: - N:1 com users (professional) - N:1 com users (patient)


subscriptions

Assinaturas de usuários.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - plan_id - Foreign Key → plans (opcional) - name - Nome da assinatura - amount - Valor - status - 'active', 'inactive', 'cancelled' - start_date - DATE - end_date - DATE (opcional) - billing_cycle - 'monthly', 'six_months', 'yearly' - payment_gateway_id - ID no gateway

Índices: - user_id - Index - plan_id - Index

Relacionamentos: - N:1 com users - N:1 com plans - 1:N com payments


payments

Pagamentos.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - subscription_id - Foreign Key → subscriptions (opcional) - amount - Valor - status - 'pending', 'completed', 'failed', 'cancelled' - payment_date - DATE - gateway_provider - Provedor (stripe, pagseguro, etc.) - gateway_id - ID no gateway - gateway_response - Resposta completa do gateway

Índices: - user_id - Index - subscription_id - Index - gateway_id - Index

Relacionamentos: - N:1 com users - N:1 com subscriptions


plans

Planos de assinatura.

Campos Principais: - id - Primary Key - name - Nome do plano - description - Descrição - status - 'active', 'inactive' - can_use_app - Pode usar app - can_use_web - Pode usar web - can_access_reports - Pode acessar relatórios - can_access_api - Pode acessar API - price_monthly - Preço mensal - price_six_months - Preço semestral - price_yearly - Preço anual

Relacionamentos: - 1:N com subscriptions


messages

Mensagens entre usuários.

Campos Principais: - id - Primary Key - sender_id - Foreign Key → users - receiver_id - Foreign Key → users - content - Conteúdo da mensagem - attachment_url - URL de anexo - is_read - Se foi lida - read_at - Data de leitura

Índices: - sender_id - Index - receiver_id - Index

Relacionamentos: - N:1 com users (sender) - N:1 com users (receiver)


appointments

Agendamentos entre profissional e paciente.

Campos Principais: - id - Primary Key - professional_id - Foreign Key → users - patient_id - Foreign Key → users - scheduled_at - TIMESTAMP (data/hora) - duration - Duração em minutos - status - 'pending_confirmation', 'scheduled', 'completed', 'cancelled', 'rejected' - video_call_url - URL da videochamada - meeting_id - ID da reunião - notes - Notas - rejection_reason - Motivo da rejeição

Índices: - professional_id - Index - patient_id - Index

Relacionamentos: - N:1 com users (professional) - N:1 com users (patient)


notifications

Notificações do sistema.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - type - Tipo ('message', 'appointment', 'task', 'reminder', 'system') - title - Título - message - Mensagem - is_read - Se foi lida - read_at - Data de leitura - metadata - JSONB (metadados adicionais) - reference_type - Tipo de referência - reference_id - ID da referência

Índices: - user_id - Index

Tipos Especiais: - metadata - JSONB (ex: {"key": "value"})

Relacionamentos: - N:1 com users


cycle_comments

Comentários de profissionais em ciclos.

Campos Principais: - id - Primary Key - cycle_id - Foreign Key → cycles - professional_id - Foreign Key → users - content - Conteúdo do comentário

Índices: - cycle_id - Index - professional_id - Index

Relacionamentos: - N:1 com cycles - N:1 com users (professional)


user_auth_providers

Identidades OAuth associadas a usuários.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - provider - 'google', 'apple' - provider_user_id - ID do usuário no provedor (sub do token) - email - Email (opcional, apenas informativo)

Índices: - user_id - Index - provider - Index

Constraints: - provider_user_id deve ser único por provider

Relacionamentos: - N:1 com users


email_verifications

Tokens de verificação de email.

Campos Principais: - id - Primary Key - token - Token único - user_id - Foreign Key → users - expires_at - TIMESTAMP - used - Se foi usado - used_at - Data de uso - last_resend_at - Último reenvio

Índices: - token - Unique Index - user_id - Index - last_resend_at - Index

Relacionamentos: - N:1 com users


password_resets

Tokens de reset de senha.

Campos Principais: - id - Primary Key - token - Token único - user_id - Foreign Key → users - expires_at - TIMESTAMP - used - Se foi usado - used_at - Data de uso

Índices: - token - Unique Index - user_id - Index

Relacionamentos: - N:1 com users


accounts

Contas financeiras genéricas.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - name - Nome da conta - amount - Valor - due_date - DATE - is_paid - Se foi pago - sync_id - Para sincronização mobile

Índices: - user_id - Index - sync_id - Index

Relacionamentos: - N:1 com users


professional_bank_accounts

Contas bancárias de profissionais.

Campos Principais: - id - Primary Key - professional_id - Foreign Key → users (Unique) - bank_name - Nome do banco - account_type - 'checking', 'savings' - agency - Agência - account_number - Número da conta - cpf_or_cnpj - CPF ou CNPJ

Índices: - professional_id - Unique Index

Constraints: - professional_id é único (garante 1 conta por profissional)

Relacionamentos: - 1:1 com users (professional)


availability_slots

Slots de disponibilidade de profissionais.

Campos Principais: - id - Primary Key - professional_id - Foreign Key → users - date - DATE - start_time - VARCHAR(5) (HH:MM) - end_time - VARCHAR(5) (HH:MM) - is_available - Se está disponível

Índices: - professional_id - Index

Relacionamentos: - N:1 com users (professional)


work_schedules

Horários de trabalho de profissionais.

Campos Principais: - id - Primary Key - professional_id - Foreign Key → users (Unique) - days_of_week - TEXT (JSON array) - start_time - VARCHAR(5) (HH:MM) - end_time - VARCHAR(5) (HH:MM) - appointment_duration - Duração em minutos - lunch_start_time - VARCHAR(5) (HH:MM) - lunch_end_time - VARCHAR(5) (HH:MM)

Índices: - professional_id - Unique Index

Constraints: - professional_id é único (garante 1 horário por profissional)

Relacionamentos: - 1:1 com users (professional)


courses

Cursos do marketplace (metadados editoriais e publicação).

Campos Principais: - id - Primary Key - professional_id - Profissional dono do curso - title - Título - description - Descrição - syllabus - Ementa - learning_objectives - Objetivos de aprendizagem (JSONB) - target_audience - Público alvo - prerequisites - Pré-requisitos - video_url - URL do vídeo - thumbnail_url - URL da thumbnail - duration - Duração em minutos - order - Ordem de exibição - price_amount - Preço - currency - Moeda (BRL) - access_type - Tipo de acesso (lifetime|timed) - access_days - Dias de acesso quando timed - status - Estado editorial (draft|in_review|published|rejected|unpublished) - rejection_reason - Motivo de rejeição


course_modules

Módulos do curso.

Campos Principais: - id - Primary Key - course_id - FK para courses - title - Título do módulo - order - Ordem no curso


course_lessons

Aulas dos módulos do curso, incluindo estado do pipeline de vídeo.

Campos Principais: - id - Primary Key - course_id - FK para courses - module_id - FK para course_modules - title - Título da aula - description - Descrição - mux_upload_id - ID de upload direto no Mux - mux_asset_id - ID do asset no Mux - mux_playback_id - ID de playback no Mux - video_status - pending_upload|processing|ready|errored - video_error_reason - Motivo de erro quando houver - processing_started_at - Início do processamento - ready_at - Momento em que o vídeo ficou pronto - last_mux_event_at - Último evento Mux processado - duration_sec - Duração em segundos - is_preview - Aula de preview - order - Ordem no módulo


video_webhook_events

Eventos de webhook de vídeo (Mux) com controle de idempotência e retries.

Campos Principais: - provider - Provedor (mux) - event_id - ID único do evento no provedor - event_type - Tipo do evento - status - pending|processed|failed|dead_letter - retry_count - Quantidade de tentativas - max_retries - Limite de tentativas - last_error - Último erro no processamento - payload_hash - Hash do payload - payload - Payload bruto (JSONB) - processed_at - Data/hora de processamento - dead_lettered_at - Data/hora de ida para dead-letter


course_enrollments

Matrículas de usuários em cursos.

Campos Principais: - id - Primary Key - user_id - Comprador/aluno - course_id - Curso - course_order_id - Pedido que originou a matrícula - status - active|expired|revoked|refunded - access_type - lifetime|timed - started_at - Início do acesso - ends_at - Fim do acesso (quando timed) - revoked_at - Revogação


course_lesson_progress

Progresso por aula e matrícula.

Campos Principais: - enrollment_id - Matrícula - lesson_id - Aula - progress_pct - Percentual de progresso - watched_seconds - Segundos assistidos - last_position_seconds - Última posição no player - is_watched - Marcador de aula assistida - last_seen_at - Última visualização - completed_at - Conclusão da aula

sync_logs

Logs de sincronização mobile.

Campos Principais: - id - Primary Key - user_id - Foreign Key → users - device_id - ID do dispositivo - sync_type - Tipo ('push', 'pull') - status - Status ('success', 'error') - records_count - Quantidade de registros - error - Erro (se houver) - synced_at - TIMESTAMP

Índices: - user_id - Index - device_id - Index

Relacionamentos: - N:1 com users


settings

Configurações globais do sistema.

Campos Principais: - Apenas campos padrão (ID, CreatedAt, UpdatedAt, DeletedAt)

Nota: Estrutura base para configurações futuras.


Relacionamentos

Diagrama de Relacionamentos

erDiagram
    users ||--o{ cycles : "tem"
    users ||--o| client_profiles : "tem"
    users ||--o| professional_profiles : "tem"
    users ||--o{ observations : "faz"
    users ||--o{ subscriptions : "tem"
    users ||--o{ messages : "envia/recebe"
    users ||--o{ appointments : "agenda"
    users ||--o{ notifications : "recebe"
    users ||--o{ user_auth_providers : "tem"
    users ||--o{ professional_patients : "professional"
    users ||--o{ professional_patients : "patient"
    users ||--o| professional_bank_accounts : "tem"
    users ||--o| work_schedules : "tem"
    users ||--o{ availability_slots : "tem"

    cycles ||--o{ observations : "contém"
    cycles ||--o{ cycle_comments : "tem"

    plans ||--o{ subscriptions : "gera"
    subscriptions ||--o{ payments : "tem"

    symbol ||--o{ observations : "usado_em"
    appearances ||--o{ observations : "usado_em"
    sensations ||--o{ client_profiles : "pbi_sensation"
    appearances ||--o{ client_profiles : "pbi_appearance"

Relacionamentos 1:1

  • usersclient_profiles (via user_id único)
  • usersprofessional_profiles (via user_id único)
  • usersprofessional_bank_accounts (via professional_id único)
  • userswork_schedules (via professional_id único)

Relacionamentos 1:N

  • userscycles
  • usersobservations
  • userssubscriptions
  • usersmessages (sender e receiver)
  • usersappointments (professional e patient)
  • cyclesobservations
  • cyclescycle_comments
  • planssubscriptions
  • subscriptionspayments

Relacionamentos N:M

  • usersusers (via professional_patients)
  • Um profissional pode ter vários pacientes
  • Um paciente pode estar com apenas um profissional

Índices

Índices Únicos

  • users.email - Unique Index
  • users.username - Index (permite múltiplos NULLs)
  • symbol.name - Unique Index
  • sensations.name - Unique Index
  • appearances.name - Unique Index
  • email_verifications.token - Unique Index
  • password_resets.token - Unique Index
  • client_profiles.user_id - Unique Index
  • professional_profiles.user_id - Unique Index
  • professional_patients.patient_id - Unique Index
  • professional_bank_accounts.professional_id - Unique Index
  • work_schedules.professional_id - Unique Index

Índices de Performance

  • observations.user_id - Para queries por usuário
  • observations.cycle_id - Para queries por ciclo
  • observations.date - Para queries por data
  • cycles.user_id - Para buscar ciclos do usuário
  • cycles.is_active - Para buscar ciclo ativo
  • messages.sender_id - Para buscar mensagens enviadas
  • messages.receiver_id - Para buscar mensagens recebidas
  • appointments.professional_id - Para buscar agendamentos do profissional
  • appointments.patient_id - Para buscar agendamentos do paciente

Índices Compostos

Alguns índices compostos podem ser criados para otimizar queries específicas:

-- Exemplo: Buscar observações por usuário e data
CREATE INDEX idx_observations_user_date ON observations(user_id, date);

Constraints

Foreign Keys

Todas as foreign keys são gerenciadas pelo GORM:

UserID uint `gorm:"index;not null" json:"user_id"`
User   User `gorm:"foreignKey:UserID" json:"user,omitempty"`

Unique Constraints

Campos únicos: - users.email - symbol.name - sensations.name - appearances.name - client_profiles.user_id - professional_profiles.user_id - professional_patients.patient_id

Unique condicional: - users.username - Permite múltiplos NULLs (via migração SQL)

Check Constraints

Alguns constraints podem ser adicionados via migrações SQL:

-- Exemplo: Validar user_type
ALTER TABLE users ADD CONSTRAINT check_user_type 
CHECK (user_type IN ('client', 'professional', 'admin'));

Not Null Constraints

Campos obrigatórios são definidos nos modelos:

Email string `gorm:"not null" json:"email"`
Name  string `gorm:"not null" json:"name"`

Tipos de Dados Especiais

DATE

Tipo DATE para datas civis (sem hora/timezone).

Uso: - cycles.start_date - cycles.end_date - observations.date - client_profiles.birth_date - subscriptions.start_date - subscriptions.end_date - availability_slots.date

Modelo Go:

type Date struct {
    time.Time
}

Serialização JSON: "YYYY-MM-DD"

JSONB

Tipo JSONB para dados estruturados.

Uso: - day_based_rule.allowed_symbol_ids - Array de IDs: [1, 2, 3] - day_based_rule.restricted_symbol_ids - Array de IDs: [4, 5] - notifications.metadata - Objeto JSON: {"key": "value"} - work_schedules.days_of_week - Array de números: [1, 2, 3, 4, 5]

Modelo Go:

type UIntArray []uint  // Para arrays de uint
type NotificationMetadata map[string]interface{}  // Para objetos

Soft Deletes

Maioria das tabelas suporta soft delete:

DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`

Comportamento: - GORM filtra automaticamente registros deletados - Use Unscoped() para incluir deletados


Estrutura de Tabelas

Padrão de Campos

Todas as tabelas têm campos padrão:

id          SERIAL PRIMARY KEY
created_at  TIMESTAMP NOT NULL DEFAULT NOW()
updated_at  TIMESTAMP NOT NULL DEFAULT NOW()
deleted_at  TIMESTAMP NULL  -- Soft delete

Nomenclatura

Tabelas: - Nome no singular (GORM pluraliza automaticamente) - Exceção: symbol (tabela nomeada explicitamente)

Colunas: - snake_case - Foreign keys: {model}_id (ex: user_id, cycle_id)


Queries Comuns

Buscar Ciclo Ativo

SELECT * FROM cycles 
WHERE user_id = ? AND is_active = true 
LIMIT 1;

Buscar Observações do Ciclo

SELECT * FROM observations 
WHERE cycle_id = ? 
ORDER BY date ASC;

Buscar Observação do Dia

SELECT * FROM observations 
WHERE user_id = ? AND date = ? 
LIMIT 1;

Buscar Pacientes do Profissional

SELECT u.*, pp.status 
FROM users u
INNER JOIN professional_patients pp ON u.id = pp.patient_id
WHERE pp.professional_id = ? AND pp.status = 'approved';

Buscar Perfis Pendentes

SELECT * FROM professional_profiles 
WHERE status = 'pending'
ORDER BY created_at ASC;

Performance

Índices Importantes

Para otimizar queries frequentes:

  1. Observações por usuário e data:

    CREATE INDEX idx_observations_user_date ON observations(user_id, date);
    

  2. Ciclos ativos:

    CREATE INDEX idx_cycles_user_active ON cycles(user_id, is_active) 
    WHERE is_active = true;
    

  3. Mensagens não lidas:

    CREATE INDEX idx_messages_receiver_unread ON messages(receiver_id, is_read) 
    WHERE is_read = false;
    

Otimizações Futuras

  • Particionamento: Tabelas grandes podem ser particionadas por data
  • Materialized Views: Para estatísticas complexas
  • Full-Text Search: Para busca de texto (mensagens, comentários)

Backup e Restore

Backup

pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME > backup.sql

Restore

psql -h $DB_HOST -U $DB_USER -d $DB_NAME < backup.sql

Backup com Compressão

pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME | gzip > backup.sql.gz

Referências