Skip to content

School Management Information System (SMIS)

Product Requirements Document — Core Data Model & Entity Definitions

Field Value
Document Version 1.4
Status Draft
Date April 2026
Audience Engineering, Product, Design

1. Overview

This document defines the product requirements for the core data entities of a School Management Information System (SMIS). It covers the primary domain objects — School, Campus, Section, Classroom, Student, Family, SchoolStaff, Guardian, AcademicYear, AcademicPeriod, and AcademicHoliday — along with their attributes, relationships, and business rules.

The goal is to provide a shared reference for engineering, product, and design teams so that all implementations align on data structure, terminology, and system behaviour.


2. Scope

In scope for this PRD:

  • Entity definitions and attributes for all eleven core domain objects
  • Relationships between entities
  • Business rules and constraints
  • Guardian type enumeration
  • Academic year configuration and term/semester structures

Out of scope:

  • Authentication and access control
  • Financial or fee management
  • Academic grading and results
  • Timetables and daily scheduling

3. Entity Definitions

3.1 School

A School is the top-level organisational entity in the system. It represents a registered educational institution and acts as the root owner of all other entities.

Attribute Type Required Description
id UUID Yes Unique system identifier
name String Yes Official registered name of the school
code String Yes Short alphanumeric code (unique across system)
logo_url String No URL to the school's logo image
address String Yes Physical address of the main school office
phone String Yes Primary contact phone number
email String Yes Official contact email address
website String No School website URL
accreditation_number String No National accreditation or registration number
default_currency String Yes ISO 4217 currency code used for all financial operations (e.g., GHS, USD, NGN). Set during school creation.
legacy_school_id Long No School ID from the legacy Smartsapp system. Used to map authenticated users to their school during the migration period. Unique when present.
is_active Boolean Yes Whether the school is currently active
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • A School must have at least one Campus.
  • The school code must be unique across the entire system.
  • Every school must have a default_currency set. This currency applies to all financial operations (canteen pricing, invoices, wallet balances) across the school.
  • legacy_school_id must be unique across the system when present. It maps the JWT school_id claim from the legacy auth system to a school record.

3.2 Campus

A Campus is a physical location or branch that belongs to a School. A single school may operate across multiple campuses (e.g., a main campus and a satellite campus).

Attribute Type Required Description
id UUID Yes Unique system identifier
school_id UUID (FK) Yes Reference to parent School
name String Yes Name of the campus
code String Yes Short code (unique within the school)
address String Yes Physical address of the campus
phone String No Campus-specific contact number
email String No Campus-specific email address
principal_name String No Name of the campus principal/head
is_active Boolean Yes Whether the campus is active
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • A Campus belongs to exactly one School.
  • A Campus must contain at least one Section.

Status: Pending on enforcement — the "code unique within school" constraint (column code) and the "at least one Section per Campus" rule are product requirements but are not yet enforced by a DB unique constraint or service-level validation. Duplicates and empty campuses can be created today.


3.3 Section

A Section represents a major academic division or stage within a Campus — for example, Primary, Junior High, or Senior High. Sections allow campuses to organise classrooms and students by educational stage.

Attribute Type Required Description
id UUID Yes Unique system identifier
campus_id UUID (FK) Yes Reference to parent Campus
name String Yes Display name (e.g., Primary, Junior High, Senior High)
code String Yes Short code (unique within the campus)
level_order Integer No Numeric ordering for display purposes
description String No Optional description of the section
is_active Boolean Yes Whether this section is active
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Common Section Values:

  • Creche / Nursery
  • Kindergarten
  • Primary
  • Junior High School (JHS)
  • Senior High School (SHS)

Business Rules:

  • A Section belongs to exactly one Campus.
  • Section names must be unique within a campus.

Status: Pending on enforcement — the "unique name within campus" rule is a product requirement but is not yet enforced by a DB unique constraint or service-level validation. Duplicate section names can be created today.


3.4 Classroom

A Classroom represents a specific class or form group within a Section. It is the unit to which students are assigned and where teaching and learning occurs.

Attribute Type Required Description
id UUID Yes Unique system identifier
section_id UUID (FK) Yes Reference to parent Section
name String Yes Class name (e.g., Primary 3A, JHS 2B)
code String Yes Short code (unique within the section)
academic_year String Yes Academic year the class belongs to (e.g., 2025/2026)
capacity Integer No Maximum number of students allowed
class_teacher_id UUID (FK) No Reference to the assigned SchoolStaff as class teacher
room_number String No Physical room or location identifier
is_active Boolean Yes Whether this classroom is active for the current year
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • A Classroom belongs to exactly one Section.
  • Classroom codes must be unique within a section for a given academic year.
  • A student can only be enrolled in one active classroom at a time.

Status: Pending on enforcement — the "classroom code unique within section per academic year" constraint and the "one active classroom per student" constraint are product requirements but are not yet enforced by DB constraints or service-level validation. Duplicate codes and multiple active enrollments can be created today.


3.5 Student

A Student is an individual enrolled in the school system. Students are associated with a Classroom, a Family, and one or more Guardians.

Attribute Type Required Description
id UUID Yes Unique system identifier
school_id UUID (FK) Yes Reference to School (denormalised for fast lookup)
classroom_id UUID (FK) Yes Current active classroom enrollment
family_id UUID (FK) No Reference to the student's Family record
student_id_number String Yes Human-readable student ID (unique per school)
first_name String Yes Student's first name
middle_name String No Student's middle name
last_name String Yes Student's last name (surname)
date_of_birth Date Yes Date of birth
gender Enum Yes Male / Female / Other
profile_photo_url String No URL to the student's photo
nationality String No Student's nationality
religion String No Student's religion (optional)
blood_group String No Blood group for medical records
medical_notes Text No Any relevant medical or dietary notes
enrollment_date Date Yes Date the student was first enrolled
status Enum Yes Active / Inactive / Graduated / Withdrawn / Suspended
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • A Student must belong to a School and a Classroom.
  • Student ID numbers must be unique within a school.
  • Every active student must have at least one Guardian linked.

Status: Pending on enforcement — the "student_id_number unique within school" constraint and the "active student must have ≥1 Guardian" rule are product requirements but are not yet enforced by DB constraints or service-level validation. Duplicate IDs and guardian-less active students can be created today.


3.6 Family

A Family groups students who share the same household or family unit. This enables communication and billing to be addressed at the family level rather than per-student.

Attribute Type Required Description
id UUID Yes Unique system identifier
school_id UUID (FK) Yes Reference to School
family_name String Yes Surname or household name (e.g., Mensah Family)
family_code String Yes Unique family reference code within the school
primary_address String No Primary residential address of the family
primary_phone String No Primary contact phone number for the family
primary_email String No Primary email address for the family
notes Text No Any administrative notes about the family
is_active Boolean Yes Whether this family record is active
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • A Family can have multiple Students.
  • A Family can have multiple Guardians.
  • Family codes must be unique within a school.

Status: Pending on enforcement — the "family_code unique within school" constraint is a product requirement but is not yet enforced by a DB unique constraint or service-level validation. Duplicate family codes can be created today.


3.7 SchoolStaff

SchoolStaff represents all employees of the school — including teachers, administrators, support staff, and management. Staff members can be assigned roles and linked to classrooms.

Attribute Type Required Description
id UUID Yes Unique system identifier
school_id UUID (FK) Yes Reference to School
campus_id UUID (FK) No Primary campus assignment
staff_id_number String Yes Human-readable staff ID (unique per school)
first_name String Yes Staff member's first name
middle_name String No Staff member's middle name
last_name String Yes Staff member's last name
email String Yes Official school email address
phone String No Contact phone number
date_of_birth Date No Date of birth
gender Enum No Male / Female / Other
profile_photo_url String No URL to staff photo
role Enum Yes Teacher / Administrator / Headmaster / Support / Other
department String No Department or subject area
employment_type Enum No Full-time / Part-time / Contract
hire_date Date Yes Date of first employment
status Enum Yes Active / Inactive / On Leave / Terminated
legacy_user_id Long No User ID from the legacy Smartsapp auth system. Used to map authenticated users to staff records during the migration period. Unique when present.
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • Staff IDs must be unique within a school.
  • legacy_user_id must be unique across the system when present. It maps the JWT user_id claim from the legacy auth system to a staff record.
  • A staff member can be assigned as the class teacher for multiple classrooms.
  • Staff email addresses must be unique system-wide.

Status: Pending on enforcement — the "staff_id_number unique within school" constraint is a product requirement but is not yet enforced by a DB unique constraint or service-level validation. Duplicate staff IDs can be created today. (legacy_user_id uniqueness is enforced.)


3.8 Guardian

A Guardian is an adult who is responsible for a Student. Guardians are linked to Students directly and optionally to a Family record. A student may have more than one Guardian.

Attribute Type Required Description
id UUID Yes Unique system identifier
family_id UUID (FK) No Optional reference to the Family record
guardian_type Enum Yes See Guardian Types below
first_name String Yes Guardian's first name
middle_name String No Guardian's middle name
last_name String Yes Guardian's last name
email String No Contact email address
phone_primary String Yes Primary phone number
phone_secondary String No Secondary phone number
occupation String No Guardian's occupation
employer String No Guardian's employer name
address String No Guardian's residential address
national_id String No National ID or passport number
profile_photo_url String No URL to guardian's photo
is_emergency_contact Boolean Yes Whether this guardian is an emergency contact
can_pickup_student Boolean Yes Whether this guardian is authorised to pick up the student
is_primary_contact Boolean Yes Designates the main contact guardian for the student
legacy_user_id Long No Maps to the legacy auth system's user ID. Used by the Parent App to resolve the authenticated guardian's identity. Unique when set.
is_active Boolean Yes Whether the guardian record is active
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

3.8.1 Guardian Types

Type Value Description
Father Biological or adoptive father of the student
Mother Biological or adoptive mother of the student
Stepfather Stepfather of the student
Stepmother Stepmother of the student
Grandfather Paternal or maternal grandfather
Grandmother Paternal or maternal grandmother
Uncle Uncle of the student
Aunt Aunt of the student
Older Sibling An older sibling acting as a guardian
Legal Guardian Court-appointed or legally designated guardian
Foster Parent Foster parent or caregiver
Relative Any other relative not covered by the above types
Family Friend A trusted family friend with guardianship responsibilities
Other Any other guardian relationship not covered above

Business Rules:

  • Every active student must have at least one Guardian.
  • is_primary_contact is currently stored in two places in the data model: once on the Guardian entity and once on each StudentGuardian junction row. The junction-row value is the per-student flag (a guardian can be primary for one child and not another); the Guardian-level field is a vestige. A canonical single source of truth has not been decided yet — treat the duplication as a known inconsistency and avoid relying on either column in isolation until it is resolved.
  • Multiple guardians may be linked to the same student (e.g., both Father and Mother).
  • A Guardian may be linked to multiple students (e.g., a parent with two children at the school).

3.9 AcademicYear

An AcademicYear defines the academic calendar configuration for a school. It is school-wide by default but can be overridden at the campus or section level, enabling different parts of the school to run different calendar structures.

Attribute Type Required Description
id UUID Yes Unique system identifier
school_id UUID (FK) Yes Reference to School (derived from authenticated user's token)
campus_id UUID (FK) No Reference to Campus. Null means school-wide default.
section_id UUID (FK) No Reference to Section. Null means inherits from campus or school.
name String Yes Display name (e.g., "2025-2026")
start_date Date Yes Overall start date of the academic year
end_date Date Yes Overall end date of the academic year
period_type Enum Yes Type of academic periods used (see §3.9.1)
is_active Boolean Yes Whether this academic year is currently active
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

3.9.1 Academic Period Types

Type Value Description
Term Traditional term-based system (typically 3 per year)
Semester Semester-based system (typically 2 per year)
Quarter Quarter-based system (4 per year)
Trimester Trimester-based system (3 per year)

Business Rules:

  • end_date must be after start_date.
  • The scope of an AcademicYear is determined by the combination of campus_id and section_id:
    • Both null → school-wide default
    • campus_id set, section_id null → campus-level override
    • section_id set → section-level override (system auto-populates campus_id from the section's parent campus)
  • Inheritance resolution: when looking up the effective academic year, the system walks up: section → campus → school-wide default, returning the first match.
  • The school_id is always derived from the authenticated user's JWT token and is never supplied in API requests.
  • The number of periods (terms/semesters) is a derived value — it is the count of AcademicPeriod records belonging to the year, not a stored field.

3.10 AcademicPeriod

An AcademicPeriod represents an individual term, semester, quarter, or trimester within an AcademicYear. Periods define the time boundaries for each segment of the academic calendar.

Attribute Type Required Description
id UUID Yes Unique system identifier
academic_year_id UUID (FK) Yes Reference to parent AcademicYear
name String Yes Display name (e.g., "Term 1", "Fall Semester")
start_date Date Yes Start date of the period
end_date Date Yes End date of the period
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • end_date must be after start_date.
  • Period dates must fall within the parent AcademicYear's date range.
  • Periods within the same AcademicYear must not overlap.
  • Periods are ordered by start_date — there is no explicit sequence number.

3.11 AcademicHoliday

An AcademicHoliday represents a break, holiday, or non-school period within an AcademicYear. Holidays can optionally be associated with a specific AcademicPeriod (e.g., a mid-term break) or exist at the year level (e.g., a break between terms).

Attribute Type Required Description
id UUID Yes Unique system identifier
academic_year_id UUID (FK) Yes Reference to parent AcademicYear
academic_period_id UUID (FK) No Optional reference to a specific AcademicPeriod. Null for year-level breaks (e.g., between terms).
name String Yes Display name (e.g., "Christmas Break", "Mid-Term Break")
start_date Date Yes Start date of the holiday
end_date Date Yes End date of the holiday
description String No Additional description or notes
created_at Timestamp Yes Record creation timestamp
updated_at Timestamp Yes Last update timestamp

Business Rules:

  • end_date must be after start_date.
  • Holiday dates must fall within the parent AcademicYear's date range.
  • Holidays within the same AcademicYear must not overlap.
  • Holidays inherit scope through their parent AcademicYear — a campus-specific holiday requires a campus-level AcademicYear override.

4. Entity Relationship Summary

Parent Entity Child Entity Relationship Notes
School Campus One-to-Many A school has one or more campuses
Campus Section One-to-Many A campus has one or more sections
Section Classroom One-to-Many A section has one or more classrooms
Classroom Student One-to-Many A classroom has many enrolled students
School Family One-to-Many Families are scoped to a school
Family Student One-to-Many A family groups multiple students
Family Guardian One-to-Many A family has multiple guardians
Student Guardian Many-to-Many Via student_guardian junction table
School SchoolStaff One-to-Many Staff belong to a school
Classroom SchoolStaff Many-to-One Class teacher assignment
School AcademicYear One-to-Many School-wide default academic year(s)
Campus AcademicYear One-to-Many Optional campus-level overrides
Section AcademicYear One-to-Many Optional section-level overrides
AcademicYear AcademicPeriod One-to-Many Terms/semesters within a year
AcademicYear AcademicHoliday One-to-Many Holidays/breaks within a year
AcademicPeriod AcademicHoliday One-to-Many Optional: holidays within a specific period

5. Junction Tables

5.1 student_guardian

Links Students to their Guardians in a many-to-many relationship.

Attribute Type Required Description
student_id UUID (FK) Yes Reference to Student
guardian_id UUID (FK) Yes Reference to Guardian
is_primary_contact Boolean Yes Marks the main contact for this student
is_emergency_contact Boolean Yes Authorised emergency contact for this student
can_pickup_student Boolean Yes Authorised to collect the student from school
relationship_notes Text No Additional notes on the relationship

6. API Endpoints

All endpoints require authentication via a Bearer JWT token. Responses use standard HTTP status codes: 200 (OK), 201 (Created with Location header), 204 (No Content for deletes), 404 (Not Found), 400 (Validation error).

6.1 Entity CRUD Endpoints

Each core entity follows a standard REST pattern with paginated list, get-by-ID, create, update, and delete.

Entity Base Path Tag
School /api/school/schools School – Schools
Campus /api/school/campuses School – Campuses
Section /api/school/sections School – Sections
Classroom /api/school/classrooms School – Classrooms
Student /api/school/students School – Students
Family /api/school/families School – Families
Guardian /api/school/guardians School – Guardians
Staff /api/school/staff School – Staff

Standard operations per entity:

Method Path Description
GET / List with pagination (?page=0&size=20)
GET /{id} Get by ID
POST / Create (returns 201 with Location header)
PUT /{id} Update
DELETE /{id} Delete (returns 204)

6.2 Academic Calendar Endpoints

Academic Years — /api/school/academic-years

Standard CRUD (list, get, create, update, delete) plus:

Method Path Description
GET /resolve Resolve the effective academic year for the authenticated user's school. Walks the inheritance chain: section → campus → school-wide default. Optional query params: campusId, sectionId.

Academic Periods — /api/school/academic-periods

Method Path Description
GET / List periods for an academic year. Required query param: academicYearId. Returns ordered by start date.
GET /{id} Get by ID
POST / Create — validates dates fall within parent year range and do not overlap existing periods.
PUT /{id} Update
DELETE /{id} Delete

Academic Holidays — /api/school/academic-holidays

Method Path Description
GET / List holidays for an academic year. Required query param: academicYearId.
GET /{id} Get by ID
POST / Create — validates dates fall within parent year range and do not overlap existing holidays.
PUT /{id} Update
DELETE /{id} Delete

6.3 Authenticated User Context Endpoints

These endpoints return data for the currently authenticated user based on their JWT token. They resolve the user's identity via legacy_school_id and legacy_user_id claims.

Method Path Description
GET /api/school/my-school Returns the school associated with the authenticated user's token.
GET /api/school/my-staff-profile Returns the staff profile associated with the authenticated user's token.

6.4 Parent App Endpoints

Method Path Description
GET /api/school/parent/children Returns all students linked to the authenticated parent (guardian) via the student-guardian relationship. Each child includes classroom and campus information.

7. Non-Functional Requirements

7.1 Data Integrity

  • All foreign key relationships must be enforced at the database level.
  • Soft deletes should be used where possible (is_active flag) to preserve historical data.
  • All timestamps should be stored in UTC.

7.2 Multi-Tenancy

  • All entities are scoped to a school_id to support multi-school deployments.
  • No data should be accessible across school boundaries without explicit permission.

7.3 Scalability

  • The schema should support schools with up to 10,000 students and 1,000 staff members.
  • Indexes should be created on school_id, campus_id, section_id, and classroom_id foreign keys.

8. Open Questions

# Question Owner Status
1 Should a student be allowed to appear in more than one classroom simultaneously (e.g., for cross-section subjects)? Product Open
2 What is the policy for archiving student records after graduation — hard delete, soft delete, or archive table? Engineering Open
3 Should Guardian contact information be linked to a user/login account for portal access? Product Open
4 Do we need to support multiple academic years per classroom, or create new classroom records each year? Engineering Resolved — AcademicYear entity with school → campus → section inheritance model now manages academic year configuration independently of classrooms.
5 Should SchoolStaff have login accounts by default, or is that managed separately? Product Open

9. Revision History

Version Date Author Changes
1.0 March 2026 Initial draft — all core entity definitions
1.1 March 2026 Added legacy_user_id to SchoolStaff for legacy auth system integration
1.2 March 2026 Added default_currency to School entity
1.3 April 2026 Added AcademicYear, AcademicPeriod, and AcademicHoliday entities with school → campus → section inheritance model. Resolved Open Question #4.
1.4 April 2026 Added Section 6 (API Endpoints): documented all 14 controllers including entity CRUD, academic calendar with resolve endpoint, authenticated user context (my-school, my-staff-profile), and parent app (children). Added legacy_user_id to Guardian entity. Renumbered sections 6–8 → 7–9.
1.5 2026-04-19 Sync pass against implementation: added Pending on enforcement notes to Campus (3.2), Section (3.3), Classroom (3.4), Student (3.5), Family (3.6), and SchoolStaff (3.7) to flag that several documented uniqueness and cardinality rules (campus code, section name, classroom code, student_id_number, family_code, staff_id_number, ≥1 Section per Campus, ≥1 Guardian per active student, one active classroom per student) have no DB or service-level enforcement today. Reconciled Guardian (3.8) business rules to describe the duplicate is_primary_contact storage on both Guardian and StudentGuardian — canonical source not yet resolved. No product scope changed.