Modelo de Domínio
Este documento descreve as entidades centrais do Neemias, seus atributos e relacionamentos, conforme implementado no backend (Cloudflare Workers + D1) e consumido pelo frontend (React SPA).
Glossário (Português → Inglês)
| Termo em português | Entidade / Conceito | Uso no código |
|---|---|---|
| Presença / Chamada | Attendance — marcar aluno como presente ou ausente | AttendanceEvent.actionType: MARK_PRESENT, MARK_ABSENT |
| Aluno / Estudante | Student — criança registrada no sistema | Student entity, students table |
| Turma | Class — faixa etária (ex: Berçário, Juniores) | Class entity, classes table |
| Aula | ClassSlot (recorrente) + ClassSession (ocorrência concreta) | ClassSlot (dia+horário), ClassSession (data específica) |
| Núcleo / Célula | Nucleus — pequeno grupo por região | Nucleus entity, nuclei table |
| Região | NucleusRegion — 7 cores fixas | Azul Celeste, Azul, Amarela, Branca, Vermelha, Laranja, Verde |
| Responsável | Guardian — pai/mãe/responsável pelo aluno | guardianName, guardianNameAlt |
| Frequência | Report — presença semanal, mensal, por aluno | ReportsPage — Frequência Semanal, por Aula, por Aluno |
| Membresia | Family membership status | MEMBRO, NAO_MEMBRO, DESCONHECIDO |
Entidades
Student (Aluno)
Tabela: students (D1 SQLite) / SQLite WASM table: students (local)
| Atributo | Tipo | Descrição |
|---|---|---|
student_id | TEXT PK | Identificador único |
display_name | TEXT | Nome de exibição do aluno |
photo_ref | TEXT | Referência da foto |
status | TEXT | ACTIVE ou DELETED |
deleted_justification | TEXT | Justificativa obrigatória (10-500 chars) ao excluir |
guardian_name | TEXT | Nome do responsável principal |
guardian_name_alt | TEXT | Nome do responsável alternativo |
birth_date | TEXT | Data de nascimento |
phones | JSON array | Lista de telefones (mín. 1, máx. 3) |
family_membership_status | TEXT | Status de membresia familiar |
address_street | TEXT | Logradouro |
address_number | TEXT | Número |
address_complement | TEXT | Complemento |
address_neighborhood | TEXT | Bairro |
address_city | TEXT | Cidade |
address_state | TEXT | Estado |
address_zip | TEXT | CEP |
class_id | TEXT FK | Referência para classes |
nucleus_participates | INTEGER | 0 ou 1 |
nucleus_region | TEXT | Região do núcleo |
nucleus_name | TEXT | Nome do núcleo |
allergies | TEXT | Alergias |
special_needs | TEXT | Necessidades especiais |
Regras: Soft-delete com justificativa obrigatória (10-500 chars). Histórico de presença preservado após exclusão. Índice: (status, updated_at DESC).
Class (Turma)
Tabela: classes (D1 SQLite) / Dexie table: classes (IndexedDB)
| Atributo | Tipo | Descrição |
|---|---|---|
class_id | TEXT PK | Identificador único |
name | TEXT UNIQUE | Nome da turma |
age_min | INTEGER | Idade mínima |
age_max | INTEGER | Idade máxima |
status | TEXT | ACTIVE ou DELETED |
Seed padrão: Berçário, Maternal Infantil, Jardim de Infância, Infantil, Juniores 1, Juniores 2, Pré-Adolescentes.
Regras: Soft-delete via status = 'DELETED'. Apenas ADMIN pode criar/editar.
ClassSlot (Aula recorrente)
Tabela Dexie: classSlots (IndexedDB, frontend apenas)
| Atributo | Tipo | Descrição |
|---|---|---|
slot_id | TEXT PK | Identificador único |
day_of_week | INTEGER | 0=Dom, 1=Seg, ..., 6=Sáb |
start_time | TEXT | Horário de início (HH:MM) |
label | TEXT | Rótulo descritivo |
session_date | TEXT? | Data de aula avulsa (YYYY-MM-DD), undefined para recorrente semanal |
ClassSession (Ocorrência concreta de aula)
Tabela Dexie: classSessions (IndexedDB, frontend apenas)
| Atributo | Tipo | Descrição |
|---|---|---|
session_id | TEXT PK | Identificador único |
slot_id | TEXT FK | Referência para ClassSlot |
session_date | TEXT | Data da sessão (YYYY-MM-DD) |
AttendanceEvent (Evento de Presença)
Tabela: attendance_events (D1 SQLite) / Dexie table: attendanceEvents (IndexedDB)
| Atributo | Tipo | Descrição |
|---|---|---|
event_id | TEXT PK | Identificador único |
student_id | TEXT FK | Referência para students |
class_session_id | TEXT FK | Referência para ClassSession (frontend) |
action_type | TEXT | MARK_PRESENT ou MARK_ABSENT |
happened_at | TEXT | Timestamp do evento |
actor_user_id | TEXT FK | Usuário que executou a ação |
actor_role | TEXT | Papel do ator no momento |
server_timestamp | TEXT | Timestamp do servidor |
is_conflict_loser | INTEGER | 0 ou 1 — perdedor de resolução de conflito |
conflict_superseded_by | TEXT? | Ponteiro para evento vencedor (frontend) |
Regras: Event-sourced — toda presença é um evento imutável. Projeção deriva estado atual. Índice: (student_id, server_timestamp DESC).
Nucleus (Núcleo)
Tabela: nuclei (D1 SQLite)
| Atributo | Tipo | Descrição |
|---|---|---|
nucleus_id | TEXT PK | Identificador único |
region | TEXT | Uma das 7 regiões fixas |
name | TEXT | Nome do núcleo |
status | TEXT | ACTIVE ou DELETED |
Regiões fixas: Azul Celeste, Azul, Amarela, Branca, Vermelha, Laranja, Verde.
Regras: Soft-delete via status = 'DELETED'. Apenas ADMIN pode criar/editar.
User (Usuário)
Tabela: users (D1 SQLite). Desde v0.56.0, papéis são armazenados em tabela separada user_roles.
UserRole (v0.56.0+)
Tabela: user_roles — tabela de junção para suporte multi-papel.
| Atributo | Tipo | Descrição |
|---|---|---|
user_id | TEXT FK | Referência para users(user_id) |
role | TEXT | Nome do papel (ex: CHAMADOR) |
created_at | TEXT | ISO 8601 |
PK: (user_id, role)
EventRegistration (Inscrição em Evento)
Tabela: event_registrations (v0.46.0+).
| Atributo | Tipo | Descrição |
|---|---|---|
registration_id | TEXT PK | Identificador único |
event_id | TEXT FK | Referência para events |
student_id | TEXT FK | Referência para students |
status | TEXT | REGISTERED, CONFIRMED, CANCELLED |
created_at | TEXT | ISO 8601 |
Tabela: users (D1 SQLite) / Dexie table: users (IndexedDB)
| Atributo | Tipo | Descrição |
|---|---|---|
user_id | TEXT PK | Identificador único |
email | TEXT UNIQUE | E-mail do usuário |
display_name | TEXT | Nome de exibição |
role | TEXT | ADMIN, CHAMADOR, RELATORIOS ou CADASTRO |
status | TEXT | ACTIVE ou DELETED |
password_hash | TEXT | Hash PBKDF2 no formato pbkdf2:<hexSalt>:<hexHash> |
Role (Papel / Perfil)
Definido em packages/permissions/index.ts — fonte única da verdade.
| Papel | Código | Permissões principais |
|---|---|---|
| Admin | ADMIN | Acesso total: CRUD alunos/turmas/núcleos, chamada, relatórios, seed |
| Chamador | CHAMADOR | Marcar presença, buscar alunos |
| Relatórios | RELATORIOS | Visualizar relatórios de frequência |
| Cadastro | CADASTRO | Criar e editar alunos e aulas |
Relacionamentos
User ──┐
│ actor_user_id
▼
AttendanceEvent ◄──── Student ──────── Class
│ │
│ │ class_id
│ ▼
│ Class
│
│ ┌── Nucleus (nucleus_participates)
│ │
└──────────────────┘- Student → Class:
N:1viaclass_idFK. - Student → Nucleus: Participação opcional (
nucleus_participates0/1). - AttendanceEvent → Student:
N:1viastudent_idFK. - AttendanceEvent → User:
N:1viaactor_user_id— todo evento registra quem o gerou. - ClassSlot → ClassSession:
1:N— um slot recorrente gera múltiplas sessões concretas. - ClassSession → AttendanceEvent:
1:N— cada sessão tem múltiplos eventos de presença.
Event Sourcing e Soft-Delete
- Event Sourcing: Toda mutação de entidade principal (students, attendance) gera um evento imutável. O estado atual é uma projeção derivada desses eventos. Tabelas auxiliares:
student_events,user_events. - Soft-delete: Nenhuma entidade principal é fisicamente removida. O campo
statustransita paraDELETED. Justificativa obrigatória para exclusão de alunos.
Fontes: workers/CONTEXT.md, app/CONTEXT.md