Skip to main content

Architecture Overview

SyncAD is a multi-tenant school administration platform with four distinct client applications consuming a unified NestJS API.

System Diagram

                        ┌─────────────────────────────────────────────────────┐
│ AWS Infrastructure │
│ RDS (PostgreSQL) · S3 · SES · SNS · FCM · Route53 │
└─────────────────────────────────────────────────────┘


┌──────────────────────────────────┼──────────────────────┐
│ │ │
┌─────┴──────┐ ┌──────┴───────┐ ┌──────┴───────┐
│ Central │ │ School DB │ │ SCS Service │
│ Database │◄─────────────────►│ (per-school│ │ (Go) │
│ (meta) │ getDbFromSchoolId│ isolated) │ │ Provisioning│
└────────────┘ └─────────────┘ └──────────────┘
│ │
┌───────────┴──────────────────────────────────────────────────────────────┐
│ │ │
┌────┴────┐ ┌────────────┐ ┌──────────┴─────┐ ┌──────────────────────┐
│ NestJS │ │ School │ │ Super Admin │ │ SCS Service (Go) │
│ API │◄──│ Admin UI │ │ (Next.js 16) │ │ POST /school │
│ :3001 │ │ (Next.js │ │ Provisioning │ │ domain/SSL/DB setup│
└────┬────┘ │ 14/MUI) │ └────────────────┘ └──────────────────────┘
│ └────────────┘
│ ┌──────────┐
├───────────────────────────────────────────────────────────────►│ Redis │
│ │ Adapter │
│ Socket.IO (bus tracking) └────┬─────┘
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐
│ │ Parents │ │ Teachers │ │ Drivers │
└────────►│ App │ │ App │ │ App │
│ (Flutter) │ │ (Flutter) │ │ (Flutter) │
└──────────────┘ └──────────────┘ └───────┬───────┘
:3000 :3001 │
▲ ▲ Socket.IO
│ │ (GPS)
┌─────┴─────────────────────┴──────────────────┐
│ REST API (JWT Bearer) │
│ /parent/* /teacher/* /driver/* │
└────────────────────────────────────────────────┘

Components

Central Database

PostgreSQL instance containing:

  • School — school registration, config, subdomain
  • User, EmployeeUserSchool — user accounts and school assignments
  • Role, Module, Role_Module_Permission — RBAC model
  • Session, RefreshToken — auth sessions
  • Admin-level lookup data (districts, boards, etc.)

Per-School Databases

Each school gets its own PostgreSQL database with 50+ tables:

  • Student, StudentClass, StudentDivision — student records
  • Attendance, Exam, Result, FeeLedger, LibraryBookIssue
  • Bus, BusRoute, BusStop, Trip, TripStop
  • Announcement, LeaveApplication, Message

Isolation ensures data privacy between schools — a compromise of one school's DB does not expose others.

NestJS API

Unified API at apps/api serving all 4 platforms. Routes requests to the correct school DB via getDbFromSchoolId() extracted from the JWT schoolId claim.

Modules: school-admin, parent, teacher, driver, bus-tracking, messaging, aws, super-admin

School Admin UI

Next.js 14 application (apps/school-admin-ui) with MUI components, Redux Toolkit state management, and TanStack Query for server state. Used by school staff for day-to-day operations.

Super Admin UI

Next.js 16 application (apps/super-admin) with shadcn/ui and Tailwind CSS. Used by Metaonus ops team to provision new schools and manage cross-school configuration.

Mobile Apps

Three Flutter applications — parents, teachers, and drivers — sharing the same MVVM architecture with Provider for state management, Dio for HTTP, and GetIt for dependency injection.

SCS (School Creation Service)

Go service (apps/scs) that automates school onboarding:

  1. Creates per-school PostgreSQL database
  2. Provisions subdomain in Route53
  3. Issues Let's Encrypt SSL certificate
  4. Runs initial Drizzle migrations
  5. Registers school in central DB

Data Flow Examples

Auth Flow

  1. Mobile app sends POST /{role}/user-auth/login with phone + OTP
  2. API validates OTP, generates JWT (15min access) + refresh token
  3. JWT contains: userId, role, schoolId
  4. Subsequent requests attach Authorization: Bearer <jwt>
  5. getDbFromSchoolId() reads schoolId from JWT to route to correct DB

Bus Tracking Flow

  1. Driver starts trip: POST /driver/bus-tracking/trips/{id}/start
  2. Driver app connects Socket.IO to /bus-tracking
  3. Emits join-bus-room with school/bus IDs
  4. App streams GPS via update-location events (every 5s)
  5. Socket.IO broadcasts to parents subscribed to that bus
  6. Parents app renders live location on OpenStreetMap

Key Design Decisions

DecisionRationale
Per-school isolated DBsHard data isolation between schools; simpler compliance
getDbFromSchoolId() routingSingle API codebase, no tenant ID in every query
JWT contains schoolIdAvoids extra DB lookup per request
Redis adapter for Socket.IOEnables horizontal scaling of API servers
Go for SCS serviceFast startup, low memory, simple deployment
Flutter for all 3 mobile appsShared architecture, faster development
Socket.IO for bus trackingBidirectional, auto-reconnect, room semantics