Frontend Architecture
Purpose
This document describes the frontend architecture for Chilarai.
It defines the structure, patterns, and decisions for building the web application using Next.js.
The frontend architecture is designed to support:
- School administrators
- Teachers
- Parents
- Platform administrators
- Future student-facing experiences
- Future agent-assisted experiences
The frontend must remain simple, modular, permission-driven, and aligned with the backend domain model.
Technology Stack
Framework
- Next.js
Language
- TypeScript
Styling
- Tailwind CSS
Component Foundation
- shadcn/ui
Server State
- TanStack Query
Application State
- React Context
Forms
- React Hook Form
Validation
- Zod
Authentication
- Keycloak
API Style
- REST APIs
Architecture Goals
The frontend architecture should:
- Stay aligned with backend domain modules
- Avoid unnecessary frontend complexity
- Support permission-driven navigation
- Support users with multiple school memberships
- Support school context switching
- Keep server state and application state separate
- Provide a consistent application shell
- Support future agent experiences
- Remain ready for internationalization
- Support responsive web experiences
Rendering Strategy
Chilarai uses a hybrid Next.js rendering strategy.
Public Pages
Public pages may use static rendering or server-side rendering.
Examples:
- Landing Page
- Pricing Page
- Public Help Pages
- Marketing Pages
Authenticated Application Pages
Authenticated application pages primarily use client-side rendering because they depend on:
- Active user
- Active school context
- Permissions
- Dynamic data
- User-specific navigation
Examples:
- Dashboard
- Students
- Admissions
- Attendance
- Fees
- Administration
Application Structure
The frontend is organized by business capability.
Example structure:
src
├── app
├── modules
├── providers
├── shared
└── api
app
Contains routing, route layouts, and application initialization.
modules
Contains business capability implementations.
Examples:
modules
├── students
├── admissions
├── academics
├── attendance
├── fees
├── staff
└── administration
providers
Contains application-level providers.
Examples:
providers
├── school-context
├── auth-context
├── theme-context
└── query-provider
shared
Contains reusable UI components, hooks, utilities, and shared frontend infrastructure.
api
Contains REST API client wrappers.
UI components must not call fetch directly.
Frontend Module Structure
Each frontend module should follow a consistent structure.
Example:
modules/students
├── pages
├── components
├── hooks
├── api
└── schemas
pages
Module-specific route-level pages.
components
Module-specific UI components.
hooks
Module-specific React hooks.
api
Module-specific API access wrappers.
schemas
Module-specific frontend validation schemas or references to shared schemas.
Module Boundary Principle
Frontend modules should mirror backend domain modules where practical.
Examples:
Backend Student Module
↔
Frontend Students Module
Backend Admission Module
↔
Frontend Admissions Module
Backend Fees Module
↔
Frontend Fees Module
This keeps the mental model consistent across frontend and backend development.
State Management
Frontend state is divided into two categories:
- Server State
- Application State
These categories must not be mixed.
Server State
Server State is data that originates from backend APIs.
Examples:
- Students
- Admissions
- Staff
- Attendance
- Fees
- Academic Years
- Academic Structure
- School Configuration
Server State is managed using TanStack Query.
TanStack Query is responsible for:
- Fetching data
- Caching data
- Loading states
- Error states
- Refetching
- Query invalidation
- Pagination
- Background refresh
Application State
Application State is state that exists only in the browser.
Examples:
- Active School Context
- Theme
- Sidebar State
- Right Workspace Panel State
- Session UI Preferences
Application State is managed using React Context.
State Management Diagram
State Ownership Rules
TanStack Query Owns
- Business data
- API results
- Lists
- Details
- Search results
- Paginated results
Examples:
students
student-details
admissions
fees
attendance
React Context Owns
- Current school
- Theme
- Layout state
- UI preferences
Prohibited Pattern
Business data must not be copied from TanStack Query into React Context or another frontend store.
Invalid example:
Students fetched by TanStack Query
↓
Copied into Context
This creates multiple sources of truth and must be avoided.
School Context Management
A user may belong to multiple schools.
The frontend maintains a single active school context.
Example:
User
├── School A
├── School B
└── School C
At any point in time:
Active School = School A
All application pages operate within the active school context.
School Switching
Users must be able to switch school context from the global application shell.
When the active school changes:
- Active school context is updated
- User permissions are refreshed
- Navigation is rebuilt
- TanStack Query cache keys change
- Relevant queries are invalidated
- Page data is reloaded
School Context Query Pattern
Queries that are school-specific must include schoolId in the query key.
Example:
["students", schoolId]
["admissions", schoolId]
["fees", schoolId]
["attendance", schoolId]
This ensures that cached data from one school is not reused for another school.
School Context Diagram
API Communication
The frontend communicates with the backend through REST APIs.
GraphQL is not used for the initial implementation.
All API communication must go through API client wrappers.
Example:
api
├── students-api.ts
├── admissions-api.ts
├── attendance-api.ts
├── fees-api.ts
└── staff-api.ts
UI components must not directly call backend endpoints.
API Client Responsibilities
API client wrappers are responsible for:
- Calling REST endpoints
- Passing school context where required
- Handling request serialization
- Handling response deserialization
- Returning typed data
- Normalizing API errors where appropriate
Forms Strategy
Chilarai uses React Hook Form for forms.
Examples:
- Student Form
- Admission Form
- Staff Form
- Fee Structure Form
- School Settings Form
React Hook Form is responsible for:
- Field state
- Dirty state
- Touched state
- Form submission
- Reset behavior
- Nested form structures
- Field arrays
Validation Strategy
Chilarai uses Zod for validation.
Validation is split into three layers.
Shared Structural Validation
Shared Zod schemas validate:
- Required fields
- Field formats
- Length constraints
- Object structure
- Basic enum values
These schemas live in shared-validation.
They are used by both:
- Frontend forms
- Backend request validation
Backend Business Validation
Business rules that require database access or module coordination are enforced only on the backend.
Examples:
- Student already enrolled in academic year
- Admission class does not belong to school
- Fee structure does not exist
- User does not have required school membership
Backend Authorization Validation
Authorization is always enforced on the backend.
Frontend checks are only used to improve the user experience.
Forms and Validation Diagram
Authentication
Authentication is handled by Keycloak.
The frontend is responsible for:
- Redirecting users to login
- Handling authentication callback
- Maintaining authenticated session state
- Passing access tokens to API clients
- Handling logout
The frontend must not manage passwords or authentication secrets.
Authorization
Authorization is permission-driven.
The frontend uses permissions to determine:
- Visible navigation items
- Visible buttons
- Available actions
- Accessible screens
The backend remains the final authority for authorization.
Roles vs Permissions
Roles are administrative groupings.
Examples:
- Parent
- Teacher
- School Admin
- Principal
- Accountant
Permissions drive UI behavior.
Examples:
- students.read
- students.manage
- admissions.review
- fees.manage
- attendance.manage
- school.settings.manage
Frontend rendering must depend on permissions, not raw role names.
Permission Driven Navigation
The left navigation is built from a navigation configuration.
Each navigation item declares the required permission.
Example:
Students
Required Permission:
students.read
If the user has the permission, the item is visible.
If the user does not have the permission, the item is hidden.
Permission Driven Navigation Diagram
Multi-Role Navigation
A user may have multiple roles in the same school.
Example:
User
├── Parent
└── Teacher
The navigation must show the union of permissions granted to all roles.
The frontend must not force the user to switch between role modes such as:
- Parent Mode
- Teacher Mode
- Admin Mode
Instead, the user sees all capabilities allowed by their permissions.
Application Shell
The application uses a single shared shell.
The shell contains:
- Header
- Left Navigation
- Main Workspace
- Right Workspace Panel
- Footer
Modules render inside the shell.
Modules must not create their own top-level application shells.
Layout Model
Header
The header contains global actions.
Examples:
- School Switcher
- Notifications
- User Menu
- Future Global Search
The header remains visible across authenticated pages.
Left Navigation
The left navigation is:
- Grouped by business capability
- Permission-driven
- Dynamic
- School context aware
Example groups:
- Dashboard
- Students
- Academics
- Attendance
- Fees
- Administration
When school context changes, left navigation is recalculated.
Main Workspace
The main workspace is used for primary workflows.
Examples:
- Student Management
- Admission Management
- Fee Structure Management
- Attendance Entry
- Reports
- Settings
Complex workflows should use dedicated pages.
Right Workspace Panel
The right workspace panel is used for lightweight and contextual experiences.
Examples:
- Notifications
- Agent Conversations
- Quick Views
- Tasks
- Approvals
- Help
- Activity Feed
The right workspace panel is not intended for heavy data-entry workflows.
Workspace Usage Rules
Use dedicated pages for:
- Create Student
- Edit Student
- Create Admission
- Edit Admission
- Fee Structure Management
- Attendance Entry
- School Settings
Use the right workspace panel for:
- Notifications
- Agent
- Quick Student Preview
- Approval Queue Preview
- Help
- Contextual Activity
Footer
A small footer is included in the application shell.
Examples:
- Copyright
- Version
- Legal Links
The footer should remain visually minimal and unobtrusive.
Design System
Tailwind CSS is used for styling.
shadcn/ui is used as the component foundation.
Shared components should be built on top of shadcn/ui primitives.
Examples:
- Data Table
- Form Field
- Page Header
- Empty State
- Confirm Dialog
- Status Badge
- Permission Gate
Component Architecture
Components should be organized into three categories.
Shared Components
Reusable across the application.
Examples:
- Button wrappers
- Data table
- Page header
- Permission guard
- Loading state
- Empty state
Module Components
Specific to a domain module.
Examples:
- StudentCard
- AdmissionStatusBadge
- FeeSummaryCard
Page Components
Route-level composition components.
Examples:
- StudentListPage
- AdmissionReviewPage
- FeeStructurePage
Frontend Caching
TanStack Query is the only frontend caching mechanism.
Custom frontend caches should not be introduced.
The frontend must not directly use Redis.
Redis is a backend concern.
Cache Invalidation
Mutations must invalidate affected queries.
Examples:
Create Student
↓
Invalidate students query
Approve Admission
↓
Invalidate admissions query
↓
Invalidate students query
Record Payment
↓
Invalidate student fees query
Error Handling
Frontend error handling should support:
- Field-level validation errors
- Form submission errors
- Page-level data loading errors
- Authorization errors
- Unexpected failures
Common error UI components should be shared.
Examples:
- Error Alert
- Forbidden Page
- Not Found Page
- Retry Panel
File Uploads
File upload flows are required for:
- Student Photos
- Staff Photos
- Admission Documents
- Certificates
- School Assets
The frontend should upload files only through backend-supported upload flows.
The frontend should not directly expose storage provider details such as MinIO bucket names.
File downloads should use backend-generated pre-signed URLs.
Internationalization
The frontend must be internationalization-ready.
Initial language:
- English
Future languages may include:
- Assamese
- Hindi
- Bengali
User-facing strings should not be hardcoded deeply inside business logic.
Accessibility
The frontend should follow accessible UI practices.
Guidelines:
- Keyboard navigable components
- Visible focus states
- Accessible labels
- Semantic HTML
- Sufficient color contrast
- Screen-reader-friendly controls
shadcn/ui components should be used in an accessible manner.
Responsive Design
The primary target is desktop and tablet usage for school administration.
The application should remain usable on smaller screens where practical.
Mobile-first experiences may be introduced later for parent and student workflows.
Testing Strategy
The frontend should support:
- Unit tests for utilities and hooks
- Component tests for shared components
- Integration tests for important workflows
- End-to-end tests for critical journeys
Initial focus should be on:
- Login
- School switching
- Student management
- Admission workflow
- Fee collection
Build and Deployment Considerations
The frontend is built as part of the Nx monorepo.
The frontend should be independently deployable from the backend.
Environment-specific configuration should be externalized.
Examples:
- API base URL
- Keycloak configuration
- Feature flags
- Public environment settings
Future Considerations
The following capabilities may be introduced later:
- Agent-first workflows
- Global search
- Offline support
- Mobile application
- Parent-specific portal
- Student portal
- Advanced dashboards
- Real-time notifications
These capabilities should not require a redesign of the frontend architecture.
Summary
Core decisions:
- Use Next.js for the frontend.
- Use TypeScript throughout the frontend codebase.
- Use Tailwind CSS and shadcn/ui for UI development.
- Organize modules by business capability.
- Use TanStack Query for server state.
- Use React Context for application state.
- Use REST APIs for backend communication.
- Use React Hook Form for forms.
- Use shared Zod schemas for structural validation.
- Keep business validation on the backend.
- Use permission-driven navigation.
- Use a single application shell.
- Support school context switching.
- Use a dynamic left navigation.
- Use a right workspace panel for contextual experiences.
- Use dedicated pages for complex workflows.
- Keep the frontend internationalization-ready.