Table of Contents
- The Challenge
- Architecture & Solution
- Tech Stack
- 16 Domain Modules
- RBAC — 17 Roles
- Key Engineering Decisions
- API Design
- Security Model
- Database Design
- Testing Strategy
- Deployment
- Roadmap
The Challenge
Modern Indian schools operate under a maze of complexity: CBSE, ICSE, State Board, Cambridge IGCSE, and IB curriculums each have different grading policies and compliance requirements. Admin staff juggle fee collection, timetable management, UDISE statutory reporting, hostel allocations, and parent communications — often across disconnected spreadsheets and paper registers.
Generic school administration packages fall apart under real operational load:
- Monolithic codebase → one bad feature breaks the whole system
- No offline support → schools with poor connectivity lose data
- No audit trail → compliance violations with no accountability
- Hardcoded role structures → can't model the nuanced hierarchy of Indian schools
- Concurrent bulk operations (attendance for 1,200 students at once) cause request timeouts and data corruption
The requirement: Build an ERP that handles the full operational lifecycle of an Indian school — admissions, academics, attendance, examinations, payroll, compliance, and welfare — as a maintainable, extensible system that teams can actually own.
Architecture & Solution
Aatmanova is engineered as a Modular Monolith — a single deployable unit where each feature lives in a completely self-contained domain module. Each module has its own Models, Services, Controllers, Policies, Events, Repositories, DTOs, Jobs, and Tests. Modules communicate exclusively through Events and shared Service Interfaces — never through direct Model imports across boundaries.
Module Isolation Principle
Every module passes the deletion test: you can remove the entire app/Modules/Finance/ directory and zero other modules are affected. Features communicate through:
- Laravel Events — for side-effects (withdrawal saga, notifications)
- Shared Service Interfaces — for cross-module data reads without direct coupling
- HydrationService — resolves entity names across module boundaries without importing foreign Models
Offline-First Architecture
Schools in rural India often face intermittent connectivity. The system includes a Sync Queue — a transactional outbox that stores mutations locally and replays them against the server when connectivity is restored, with full conflict resolution and idempotency guarantees.
Tech Stack
| Layer | Technology | Version | Role |
|---|---|---|---|
| Backend Framework | Laravel | 11.x | Application orchestration, DI, queue, events |
| Language | PHP | 8.3+ | Business logic, type-safe services |
| Database | PostgreSQL | 16+ | Primary data store, ACID transactions |
| Cache & Queue | Redis | 7.x | Job queue, session store, rate limit counters |
| Frontend | Vue 3 + Inertia.js | Latest | Reactive SPA with server-side routing |
| Build Tool | Vite | 5.x | Hot module replacement, production bundling |
| UI Styling | Tailwind CSS | 3.x | Utility-first, consistent indigo-600 brand |
| Reactive Components | Alpine.js | 3.x | Lightweight interactivity for Blade views |
| API Auth | JWT (php-open-source-saver) | 2.x | Stateless API authentication |
| Web Auth | Laravel Sanctum | Latest | Session-based auth for web routes |
| Testing | Pest PHP + PHPUnit | Latest | Unit, feature, and module integration tests |
| API Specification | OpenAPI | 3.1 | Full API contract (openapi/sms.yaml) |
| Secrets Management | HashiCorp Vault | Compatible | Production secrets isolation |
| Containerization | Docker + Docker Compose | Latest | Dev/prod parity |
| Process Supervision | Supervisor | Latest | Queue worker management in production |
16 Domain Modules
Each module is a complete vertical slice following the same internal structure:
app/Modules/
├── Auth/ # JWT auth, Sanctum, RBAC, MFA scaffolding, session management
├── Student/ # Enrollments, profiles, documents, withdrawal SAGA pattern
├── Staff/ # HR, recruitment, leaves, appraisals, salary slips, payroll
├── Academic/ # CBSE/ICSE/State/Cambridge/IB boards, classes, subjects, timetable
├── Attendance/ # Daily attendance, bulk entry with optimistic locking, async batching
├── Examination/ # Exam scheduling, marks entry, grade computation, result generation
├── Finance/ # Fee structures, concessions, payments, receipts, async bulk payroll
├── Library/ # Book catalogue, issue/return, overdue fine calculation
├── Transport/ # Routes, vehicles, student assignments, GPS-ready stubs
├── Hostel/ # Room inventory, student allocations, warden management
├── Communication/ # Circulars, announcements, parent notifications
├── Events/ # School events, inter-school competitions, registrations
├── Counseling/ # Student welfare cases, counselor assignment, case tracking
├── Compliance/ # UDISE reports, statutory registers, safety certificates
├── Facilities/ # Asset inventory, maintenance scheduling, work orders
└── Reports/ # Dynamic templates, PDF/Excel export, configurable outputPer-module structure (consistent across all 16):
ModuleName/
├── Controllers/ # Thin HTTP controllers — no business logic
├── DTOs/ # Data Transfer Objects with PII masking
├── Events/ # Domain events emitted on state changes
├── Jobs/ # Async background jobs for bulk operations
├── Listeners/ # Event listeners for cross-module side-effects
├── Models/ # Eloquent models with observers
├── Observers/ # Auto audit logging, version increment
├── Policies/ # RBAC authorization gates per resource
├── Providers/ # Module service provider registration
├── Repositories/ # Data access abstraction from Services
├── Requests/ # Form request validation (Zod equivalent)
├── Resources/ # API response transformers
├── Services/ # Business logic orchestration
├── Tests/ # Module-isolated feature and unit tests
├── database/
│ ├── factories/ # Eloquent model factories for testing
│ └── migrations/ # Module-specific schema migrations
└── routes/ # Module API and web routesModule Synchronization Status
All 16 modules have achieved full backend-frontend synchronization (from system audit):
| Module | Backend Service | Frontend Service | UI Integration |
|---|---|---|---|
| Auth | Full | authService.js | Live |
| Academic | Full | academicService.js | Live |
| Student | Full | studentService.js | Live |
| Attendance | Full | attendanceService.js | Live |
| Staff | Full | staffService.js | Live |
| Finance | Full | financeService.js | Live |
| Library | Full | libraryService.js | Live |
| Examination | Full | examinationService.js | Live |
| Transport | Full | transportService.js | Live |
| Hostel | Full | hostelService.js | Live |
| Communication | Full | communicationService.js | Live |
| Events | Full | eventsService.js | Live |
| Counseling | Full | counselingService.js | Live |
| Compliance | Full | complianceService.js | Live |
| Facilities | Full | facilitiesService.js | Live |
| Reports | Full | reportsService.js | Live |
RBAC — 17 Roles
The permission system is context-aware, not flat. A class teacher's attendance:write permission is silently scoped to their assigned class — they cannot record or read attendance for other sections even with the same permission flag.
| Tier | Roles |
|---|---|
| Governance | Super Admin, Board Member, Trustee, Management |
| Executive | Principal, Vice Principal |
| Academic | Academic Coordinator, Class Teacher, Subject Teacher |
| Administration | Admin Staff, Front Office, Accounts Staff |
| Welfare | Counselor, Librarian, Transport Coordinator, Warden |
| End Users | Parent/Guardian, Student (Grade 6+) |
JWT Token Lifecycle:
| Token | TTL | Storage |
|---|---|---|
| Access Token | 15 minutes | Authorization header |
| Refresh Token | 14 days | Secure cookie |
| Web Session | 4 hours | Redis-backed Sanctum |
| Mobile Session | 30 days | Redis-backed Sanctum |
Key Engineering Decisions
1. Modular Monolith over Microservices
The team is a single developer. Microservices introduce operational overhead that isn't justified at v1 scale. The modular monolith provides:
- Full domain isolation without the network overhead
- Single deployment, single database transaction boundary
- Clean extraction path if any module needs to scale independently
Each module passes the 5-file rule: a typical feature change touches ≤ 5 files, all within the same module directory.
2. Optimistic Locking on Concurrent Writes
Bulk attendance for 1,200 students submitted simultaneously causes race conditions on attendance records. Every concurrency-sensitive table has a version column. The HasOptimisticLock trait increments this on every write and rejects stale updates with a 409 Conflict — preventing silent data corruption without expensive row-level locks.
// HasOptimisticLock trait — version-based concurrent write protection
public function updateWithLock(array $data): bool {
return $this->where('id', $this->id)
->where('version', $this->version)
->update(array_merge($data, ['version' => $this->version + 1])) > 0;
}3. Dynamic Batch Routing (Sync/Async Threshold)
The HasDynamicThreshold trait makes the system self-adaptive. Any bulk operation — attendance, marks entry, payroll disbursement — automatically routes to a background job when the payload exceeds 10 records, and executes synchronously for small batches. No manual job dispatch required.
// Dynamic threshold routing
if (count($records) > self::ASYNC_THRESHOLD) {
BulkAttendanceJob::dispatch($records, $academicYearId);
return response()->json(['status' => 'queued', 'job_id' => $jobId]);
}
// < 10 records: process inline, return result immediately4. Student Withdrawal SAGA
When a student is withdrawn from school, 6 different modules must be updated atomically: Library access revoked, transport route cancelled, hostel room vacated, fee account settled, academic records archived, and staff notification sent. If any step fails, the entire saga rolls back.
This uses Laravel's transactional outbox pattern: all events are written to a domain_events table within the same DB transaction as the withdrawal record. A separate dispatcher reads and broadcasts confirmed events, guaranteeing exactly-once delivery even if the process crashes mid-saga.
5. PII Protection at the DTO Layer
Sensitive fields (Aadhaar numbers, phone numbers, emergency contacts) are masked at the DTO level — not at the API layer. This means even internal service calls return masked values for non-privileged consumers. The masking rule is: XXXX-XXXX-1234 for Aadhaar, +91-XXXXX-XXXXX for mobile.
Only roles with pii:read permission receive full values.
6. UUIDv7 Primary Keys
All tables use UUIDv7 (time-sortable) primary keys instead of auto-incrementing integers. This enables:
- Global uniqueness across future multi-tenant or sharded deployments
- Time-based ordering without a separate
created_atindex scan - No sequential ID enumeration attacks
7. Multi-Board Support
The Academic module models the full Indian school board ecosystem. Grade policies, passing criteria, grading scales, and subject structures differ between CBSE, ICSE, State Board, Cambridge IGCSE, and IB. Each board has its own GradePolicy configuration that plugs into the Examination module's grade computation engine without code changes.
API Design
The full API is documented as an OpenAPI 3.1 specification (openapi/sms.yaml), renderable via Redocly, Swagger UI, or Postman.
Design Principles:
- All endpoints versioned under
/api/v1/ - Consistent JSON envelope:
{ data: {}, meta: {}, errors: [] } - Pagination on all list endpoints (default 20, max 100)
- PII masking in list responses for non-privileged roles
- Async operations return
{ status: "queued", job_id: "..." }immediately
Core Endpoint Groups:
# Authentication
POST /api/v1/auth/login → { access_token, refresh_token }
POST /api/v1/auth/logout
POST /api/v1/auth/refresh
GET /api/v1/auth/me → Current user profile + permissions
# Students
GET /api/v1/students → Paginated list (PII-masked for non-admins)
POST /api/v1/students → Enroll new student
GET /api/v1/students/{id}
PUT /api/v1/students/{id}
DELETE /api/v1/students/{id} → Triggers Withdrawal SAGA
# Attendance (async for > 10 records)
POST /api/v1/attendance → Single or bulk entry
GET /api/v1/attendance/report → Paginated report by class/date
# Marks Entry (async for > 10 records)
POST /api/v1/marks → Bulk marks submission
GET /api/v1/results/{studentId} → Computed grades + GPA
# Finance
POST /api/v1/fee-payments → Fee collection
GET /api/v1/fee-receipts/{id} → PDF-ready receipt
POST /api/v1/payroll/disburse → Async bulk payroll (always async)
# Reports
POST /api/v1/reports/generate → Dynamic template rendering (PDF/Excel)Security Model
Layered Defense
| Layer | Control |
|---|---|
| Transport | HTTPS enforced, HSTS headers |
| Authentication | JWT (API) + Sanctum sessions (web), both short-lived |
| Authorization | Context-aware RBAC via Laravel Policies |
| Input Validation | Form Requests (every endpoint, every parameter) |
| Rate Limiting | Aggressive throttling on auth endpoints; per-route limits elsewhere |
| PII Protection | Field-level encryption + DTO masking for non-privileged reads |
| Audit Trail | Immutable audit_logs table with actor, delta, IP, session |
| Secrets | HashiCorp Vault integration for production secrets |
| CSRF | Laravel's built-in CSRF token + SameSite cookies for web routes |
Audit Log Schema
Every create/update/delete on sensitive models emits an immutable audit record:
audit_logs
├── id UUID v7
├── actor_id UUID → users.id (who)
├── action ENUM: created|updated|deleted|accessed
├── model_type string (what type)
├── model_id UUID (what record)
├── old_values JSONB (before state)
├── new_values JSONB (after state)
├── ip_address inet
├── user_agent text
└── created_at timestamptzRecords in audit_logs have no updated_at or deleted_at — they are append-only by design.
Database Design
Key Conventions
| Convention | Detail |
|---|---|
| Primary Keys | UUIDv7 — time-sortable, globally unique |
| Soft Deletes | deleted_at on all critical tables |
| Audit Columns | created_by, updated_by, deleted_by (UUID refs) |
| Optimistic Lock | version (integer) — incremented on every write |
| Timestamps | created_at, updated_at — all tables |
| Table Naming | {module}_{entity} — e.g., fee_payments, staff_leaves |
27 Seeders — Full Data Coverage
| Seeder | What it creates |
|---|---|
RoleSeeder | 17 predefined roles with permission sets |
PermissionSeeder | Full permission registry for all 16 modules |
BoardSeeder | CBSE, ICSE, State Board, Cambridge IGCSE, IB |
SchoolSeeder | Sample school profile |
SchoolClassSeeder | Classes I–XII with board associations |
SectionSeeder | Sections A–D per class |
SubjectSeeder | Core subjects mapped per board |
AcademicYearSeeder | Current academic year |
GradePolicySeeder | Grade thresholds per board |
AdminUserSeeder | Demo accounts for all 10 role tiers |
StaffSeeder | 20 sample staff profiles |
FeeStructureSeeder | Fee structures per class with concessions |
FeeConcessionSeeder | Scholarship and sibling discount rules |
BookCategorySeeder | Library book categories |
BookSeeder | 100 sample books with availability |
HostelRoomSeeder | Room inventory with capacity |
TransportRouteSeeder | Bus routes across city zones |
TransportVehicleSeeder | Fleet inventory |
LeaveTypeSeeder | Staff leave type catalogue |
ExamSeeder | Sample exam schedules |
ReportTemplateSeeder | Dynamic report template configurations |
SchoolEventSeeder | Annual school event calendar |
CircularSeeder | Sample communication circulars |
FacilitiesSeeder | Asset inventory and maintenance records |
StudentSeeder | 50 sample students |
SystemPulseSeeder | Full stress test — 2,000 students + all modules |
Testing Strategy
Three test suites provide layered coverage:
| Suite | Location | What it tests |
|---|---|---|
| Unit | tests/Unit/ | Isolated services, DTOs, utility functions |
| Feature | tests/Feature/ | Full HTTP request-response cycles |
| Modules | app/Modules/*/Tests/ | Per-domain integration tests |
# Full test run
php artisan test
# Run in parallel (faster)
php artisan test --parallel
# Run specific module
php artisan test --filter=StudentModule
# Coverage report
php artisan test --coverage
# Static analysis
./vendor/bin/phpstan analyseSystemPulse Stress Test (SystemPulseSeeder) validates the system under full load:
- 2,000 students enrolled across all 12 classes and 4 sections
- Library: books issued, returned, fines calculated for overdue
- Transport: all students assigned to routes, vehicles at capacity
- Hostel: rooms allocated, wardens assigned
- Examinations: exams created, marks entered, grades computed
- Finance: fee structures applied, payments recorded, receipts generated
- Events: school events created, students registered
- All cross-module references validated for FK integrity
Deployment
Docker (Recommended)
# One-command dev start
cp docker.env .env
docker-compose up -d --build
docker-compose exec app php artisan key:generate
docker-compose exec app php artisan jwt:secret
docker-compose exec app php artisan migrate --seed| Service | Technology | Access |
|---|---|---|
app | PHP 8.3-FPM | Port 9000 (internal) |
web | Nginx | Port 8000 (external) |
db | PostgreSQL 16 | Port 5432 (internal) |
redis | Redis 7 | Port 6379 (internal) |
worker | Laravel Queue Worker | Background |
mail | Mailpit | Port 8025 (web), 1025 (SMTP) |
Production Stack
Nginx → PHP-FPM 8.3 → Laravel 11
PostgreSQL 16 (primary + read replica)
Redis 7 (cache + queue + sessions)
Supervisor (queue workers — 3 concurrent)
Let's Encrypt (SSL via Certbot)
HashiCorp Vault (secrets)Production Deployment Checklist
composer install --optimize-autoloader --no-dev
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
npm ci && npm run build
php artisan migrate --force
php artisan queue:restartRoadmap
✅ Phase 1 — Foundation (Complete)
- 16-module architecture with full domain isolation and event-driven cross-module communication
- JWT + RBAC with 17 roles and context-aware permission scoping
- All core database schema with 27 seeders and SystemPulse validation
- OpenAPI 3.1 specification with 50+ endpoints
- Optimistic locking, audit logging, PII field-level encryption
- Async bulk operations (attendance, marks entry, payroll)
- Transactional SAGA for student withdrawal
- Offline-first sync queue architecture
- Full backend–frontend synchronization audit (all 16 modules: STABLE)
🔄 Phase 2 — Frontend Polish (In Progress)
- Vue 3 + Inertia.js SPA with role-based dashboard views
- Real-time notifications (Laravel Echo + Redis Channels)
- One-click demo login for all 10 role tiers
- Progressive Web App (PWA) for mobile access
⏳ Phase 3 — Advanced Features
- React Native mobile app (API-first)
- PDF/Excel bulk report export
- AI-powered attendance anomaly detection
- Automated UDISE report generation
- Dynamic fee reminder SMS/email workflows
🔭 Phase 4 — Scale
- Multi-tenant SaaS architecture (school-per-subdomain)
- Microservice extraction for high-traffic modules (Finance, Attendance)
- Kubernetes deployment with horizontal pod autoscaling
- Real-time GPS tracking integration for transport
- Advanced analytics dashboard with cohort analysis
Engineering Proof
Real-world validation, system demonstrations, and interface captures of the execution states.
System Demonstration
Video walkthrough detailing core logic, interactions, and system behaviors in action.
System Captures
Full Page Web Captures
Scrollable web preview simulations. Hover or scroll to preview the entire page. Use the maximize trigger to view the full resolution capture.


Architecture Feedback
Spotted a potential optimization or antipattern? Let me know.
