Domain Model
This document describes the core entities of Neemias, their attributes, and relationships, as implemented in the backend (Cloudflare Workers + D1) and consumed by the frontend (React SPA).
Glossary (Portuguese → English)
| Portuguese term | Entity / Concept | Usage in code |
|---|---|---|
| Presença / Chamada | Attendance — mark a student as present or absent | AttendanceEvent.actionType: MARK_PRESENT, MARK_ABSENT |
| Aluno / Estudante | Student — child registered in the system | Student entity, students table |
| Turma | Class — age group (e.g., Nursery, Juniors) | Class entity, classes table |
| Aula | ClassSlot (recurring) + ClassSession (concrete occurrence) | ClassSlot (day+time), ClassSession (specific date) |
| Núcleo / Célula | Nucleus — small group by region | Nucleus entity, nuclei table |
| Região | NucleusRegion — 7 fixed colors | Azul Celeste, Azul, Amarela, Branca, Vermelha, Laranja, Verde |
| Responsável | Guardian — parent/guardian of the student | guardianName, guardianNameAlt |
| Frequência | Report — weekly/monthly attendance, per student | ReportsPage — Weekly Attendance, per Class, per Student |
| Membresia | Family membership status | MEMBRO, NAO_MEMBRO, DESCONHECIDO |
Entities
Student (Aluno)
Table: students (D1 SQLite) / Dexie table: students (IndexedDB)
| Attribute | Type | Description |
|---|---|---|
student_id | TEXT PK | Unique identifier |
display_name | TEXT | Student's display name |
photo_ref | TEXT | Photo reference |
status | TEXT | ACTIVE or DELETED |
deleted_justification | TEXT | Required justification (10-500 chars) when deleting |
guardian_name | TEXT | Primary guardian's name |
guardian_name_alt | TEXT | Alternate guardian's name |
birth_date | TEXT | Date of birth |
phones | JSON array | Phone list (min 1, max 3) |
family_membership_status | TEXT | Family membership status |
address_street | TEXT | Street |
address_number | TEXT | Number |
address_complement | TEXT | Complement |
address_neighborhood | TEXT | Neighborhood |
address_city | TEXT | City |
address_state | TEXT | State |
address_zip | TEXT | ZIP code |
class_id | TEXT FK | Reference to classes |
nucleus_participates | INTEGER | 0 or 1 |
nucleus_region | TEXT | Nucleus region |
nucleus_name | TEXT | Nucleus name |
allergies | TEXT | Allergies |
special_needs | TEXT | Special needs |
Rules: Soft-delete with mandatory justification (10-500 chars). Attendance history preserved after deletion. Index: (status, updated_at DESC).
Class (Turma)
Table: classes (D1 SQLite) / Dexie table: classes (IndexedDB)
| Attribute | Type | Description |
|---|---|---|
class_id | TEXT PK | Unique identifier |
name | TEXT UNIQUE | Class name |
age_min | INTEGER | Minimum age |
age_max | INTEGER | Maximum age |
status | TEXT | ACTIVE or DELETED |
Default seed: Berçário (Nursery), Maternal Infantil (Toddlers), Jardim de Infância (Kindergarten), Infantil (Preschool), Juniores 1 (Juniors 1), Juniores 2 (Juniors 2), Pré-Adolescentes (Pre-Teens).
Rules: Soft-delete via status = 'DELETED'. Only ADMIN may create/edit.
ClassSlot (Recurring class)
Dexie table: classSlots (IndexedDB, frontend only)
| Attribute | Type | Description |
|---|---|---|
slot_id | TEXT PK | Unique identifier |
day_of_week | INTEGER | 0=Sun, 1=Mon, ..., 6=Sat |
start_time | TEXT | Start time (HH:MM) |
label | TEXT | Descriptive label |
session_date | TEXT? | One-off class date (YYYY-MM-DD), undefined for weekly recurring |
ClassSession (Concrete class occurrence)
Dexie table: classSessions (IndexedDB, frontend only)
| Attribute | Type | Description |
|---|---|---|
session_id | TEXT PK | Unique identifier |
slot_id | TEXT FK | Reference to ClassSlot |
session_date | TEXT | Session date (YYYY-MM-DD) |
AttendanceEvent (Attendance Event)
Table: attendance_events (D1 SQLite) / Dexie table: attendanceEvents (IndexedDB)
| Attribute | Type | Description |
|---|---|---|
event_id | TEXT PK | Unique identifier |
student_id | TEXT FK | Reference to students |
class_session_id | TEXT FK | Reference to ClassSession (frontend) |
action_type | TEXT | MARK_PRESENT or MARK_ABSENT |
happened_at | TEXT | Event timestamp |
actor_user_id | TEXT FK | User who performed the action |
actor_role | TEXT | Actor's role at the time |
server_timestamp | TEXT | Server timestamp |
is_conflict_loser | INTEGER | 0 or 1 — loser in conflict resolution |
conflict_superseded_by | TEXT? | Pointer to winning event (frontend) |
Rules: Event-sourced — every attendance mark is an immutable event. Projection derives current state. Index: (student_id, server_timestamp DESC).
Nucleus (Núcleo)
Table: nuclei (D1 SQLite)
| Attribute | Type | Description |
|---|---|---|
nucleus_id | TEXT PK | Unique identifier |
region | TEXT | One of 7 fixed regions |
name | TEXT | Nucleus name |
status | TEXT | ACTIVE or DELETED |
Fixed regions: Azul Celeste (Sky Blue), Azul (Blue), Amarela (Yellow), Branca (White), Vermelha (Red), Laranja (Orange), Verde (Green).
Rules: Soft-delete via status = 'DELETED'. Only ADMIN may create/edit.
User (Usuário)
Table: users (D1 SQLite) / Dexie table: users (IndexedDB)
| Attribute | Type | Description |
|---|---|---|
user_id | TEXT PK | Unique identifier |
email | TEXT UNIQUE | User's email |
display_name | TEXT | Display name |
role | TEXT | ADMIN, CHAMADOR, RELATORIOS, or CADASTRO |
status | TEXT | ACTIVE or DELETED |
password_hash | TEXT | PBKDF2 hash in pbkdf2:<hexSalt>:<hexHash> format |
Role (Papel / Profile)
Defined in packages/permissions/index.ts — single source of truth.
| Role | Code | Main permissions |
|---|---|---|
| Admin | ADMIN | Full access: CRUD students/classes/nuclei, attendance, reports, seed |
| Chamador | CHAMADOR | Mark attendance, search students |
| Relatórios | RELATORIOS | View attendance reports |
| Cadastro | CADASTRO | Create and edit students and classes |
Relationships
User ──┐
│ actor_user_id
▼
AttendanceEvent ◄──── Student ──────── Class
│ │
│ │ class_id
│ ▼
│ Class
│
│ ┌── Nucleus (nucleus_participates)
│ │
└──────────────────┘- Student → Class:
N:1viaclass_idFK. - Student → Nucleus: Optional participation (
nucleus_participates0/1). - AttendanceEvent → Student:
N:1viastudent_idFK. - AttendanceEvent → User:
N:1viaactor_user_id— every event records who generated it. - ClassSlot → ClassSession:
1:N— a recurring slot generates multiple concrete sessions. - ClassSession → AttendanceEvent:
1:N— each session has multiple attendance events.
Event Sourcing and Soft-Delete
- Event Sourcing: Every primary entity mutation (students, attendance) generates an immutable event. The current state is a projection derived from these events. Auxiliary tables:
student_events,user_events. - Soft-delete: No primary entity is physically removed. The
statusfield transitions toDELETED. Justification is mandatory for student deletions.
Sources: workers/CONTEXT.md, app/CONTEXT.md