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, subdomainUser,EmployeeUserSchool— user accounts and school assignmentsRole,Module,Role_Module_Permission— RBAC modelSession,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 recordsAttendance,Exam,Result,FeeLedger,LibraryBookIssueBus,BusRoute,BusStop,Trip,TripStopAnnouncement,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:
- Creates per-school PostgreSQL database
- Provisions subdomain in Route53
- Issues Let's Encrypt SSL certificate
- Runs initial Drizzle migrations
- Registers school in central DB
Data Flow Examples
Auth Flow
- Mobile app sends
POST /{role}/user-auth/loginwith phone + OTP - API validates OTP, generates JWT (15min access) + refresh token
- JWT contains:
userId,role,schoolId - Subsequent requests attach
Authorization: Bearer <jwt> getDbFromSchoolId()readsschoolIdfrom JWT to route to correct DB
Bus Tracking Flow
- Driver starts trip:
POST /driver/bus-tracking/trips/{id}/start - Driver app connects Socket.IO to
/bus-tracking - Emits
join-bus-roomwith school/bus IDs - App streams GPS via
update-locationevents (every 5s) - Socket.IO broadcasts to parents subscribed to that bus
- Parents app renders live location on OpenStreetMap
Key Design Decisions
| Decision | Rationale |
|---|---|
| Per-school isolated DBs | Hard data isolation between schools; simpler compliance |
getDbFromSchoolId() routing | Single API codebase, no tenant ID in every query |
JWT contains schoolId | Avoids extra DB lookup per request |
| Redis adapter for Socket.IO | Enables horizontal scaling of API servers |
| Go for SCS service | Fast startup, low memory, simple deployment |
| Flutter for all 3 mobile apps | Shared architecture, faster development |
| Socket.IO for bus tracking | Bidirectional, auto-reconnect, room semantics |