# 🦷 Dental SaaS — Arquitetura do MVP

## Diagrama de Entidades (ER simplificado)

```
clinics
  ├── professionals (N)
  ├── chairs (N)
  ├── procedures (N)
  ├── patients (N)
  │     ├── appointments (N) ──────────────────────────────────┐
  │     ├── anamneses (N)                                      │
  │     ├── patient_documents (N)                              │
  │     ├── transactions (N)                                   │
  │     └── whatsapp_logs (N)                                  │
  ├── anamnesis_templates (N)                                  │
  ├── cash_registers (N)                                       │
  │     └── transactions (N)                                   │
  └── appointments (N) ◄────────────────────────────────────────┘
        ├── chair_id → chairs
        ├── professional_id → professionals
        ├── procedure_id → procedures
        ├── transaction (1)
        ├── patient_documents (N)
        └── whatsapp_logs (N)
```

## Ordem das Migrations

| # | Arquivo | Dependências |
|---|---------|--------------|
| 1 | create_clinics_table | — |
| 2 | create_professionals_table | clinics, users |
| 3 | create_patients_table | clinics |
| 4 | create_anamneses_and_documents_tables | patients, clinics, professionals |
| 5 | create_appointments_table | clinics, patients, professionals, chairs, procedures |
| 6 | create_financial_tables | clinics, patients, appointments, users |
| 7 | create_whatsapp_logs_table | clinics, patients, appointments |

## Arquitetura de Filas (Queue Architecture)

```
SCHEDULER (a cada 30min)
  └── reminders:dispatch command
        └── Appointment::pendingReminder() query
              └── foreach appointment → SendAppointmentReminderJob::dispatch()
                                              │ onQueue('whatsapp')
                                              ▼
                                    QUEUE WORKER (whatsapp)
                                        └── WhatsappService::sendTextMessage()
                                              ├── SUCCESS → WhatsappLog(sent) + Appointment(reminder_sent=true)
                                              └── FAILURE → retry (3x) → failed_jobs + WhatsappLog(failed)
```

### Configuração das Filas (config/queue.php + .env)

```dotenv
QUEUE_CONNECTION=redis           # Redis para produção
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

# Supervisor — rodar múltiplos workers por fila:
# php artisan queue:work redis --queue=default,whatsapp --sleep=3 --tries=3
```

### Workers recomendados (Supervisor)

```ini
# /etc/supervisor/conf.d/dental-worker.conf

[program:dental-default]
command=php /var/www/artisan queue:work redis --queue=default --sleep=3 --tries=3
numprocs=2   ; 2 workers para fila geral

[program:dental-whatsapp]
command=php /var/www/artisan queue:work redis --queue=whatsapp --sleep=5 --tries=3
numprocs=1   ; 1 worker dedicado ao WhatsApp (controla rate limit da API)
```

**Por que 1 worker para WhatsApp?** A Meta API tem rate limit por número. Um único
worker processa sequencialmente, evitando burst e bloqueio de conta.

## Fluxo de Status da Consulta

```
agendado ──► confirmado ──► em_atendimento ──► realizado
    │              │                │
    └──────────────┴────────────────┴──► faltou
    └──────────────┴────────────────────► cancelado
```

## Decisões de Design

| Decisão | Motivo |
|---------|--------|
| Multi-tenant por `clinic_id` em todas as tabelas | Isolamento de dados; cada clínica acessa só os seus |
| `softDeletes` em todas as tabelas críticas | Auditoria e recuperação; nunca deletar dado odontológico |
| JSON para `schedule` e `answers` | Flexibilidade de horários/perguntas sem migrations futuras |
| `whatsapp_number` como Accessor no Model | Normalização centralizada do número |
| Job por appointment (não batch) | Falha isolada; retry individual; log granular |
| Fila `whatsapp` separada | Rate limit da API; não bloqueia operações do sistema |
| `shouldSendReminder()` no Model | Regra de negócio junto à entidade, testável unitariamente |
