0003. Single school, single current academic year per deployment
Status: Accepted
Context
Almost every academic query (class rosters, attendance, marks, timetables, section heads, prefects) needs to know "which academic year are we talking about right now" without every caller having to pass a year explicitly. Similarly, the system needed exactly one school profile per deployment rather than a multi-tenant model.
Decision
- Exactly one
schoolrow exists per deployment (a convention, not a DB constraint beyond application logic). - Exactly one
academic_yearsrow hasis_current = trueat a time, toggled viaSetCurrentAcademicYear. Nearly every academic-data query implicitly filters by this flag rather than taking an explicit year parameter. - This same flag doubles as promotion's "publish switch" - see the Promotion & Class Reassignment section of the Features page: promotion and class-shuffle write into a not-yet-current year, which stays fully editable and invisible to the rest of the app until an admin flips it current. No separate "draft" schema was needed because this invariant already provides one.
Consequences
- Not multi-tenant. Running more than one school on one OpenSchool deployment isn't supported; each school needs its own deployment (database + backend + frontend).
- The invariant is application-level, not DB-enforced. Nothing stops
two
academic_yearsrows from both havingis_current = trueat the database level; behavior if that happens is undefined and untested.SetCurrentAcademicYearis the only sanctioned way to change it. - Promotion's preview-then-commit flow gets a "draft" concept for
free. This was a deliberate reuse, not an oversight - see the
project's
docs/plan.md(Phase 5) for the original reasoning.