EN: Comprehensive technical architecture documentation for the Nzila multi-tenant gym management system.
PT: Documentação abrangente de arquitetura técnica para o sistema de gestão de ginásios multi-tenant Nzila.
Version: 2.3.0 | Last Updated: March 11, 2026
- Executive Summary
- System Overview
- Frontend Architecture
- Backend Architecture
- Data Architecture
- Security Architecture
- Module Architecture
- Integration Patterns
- Performance Considerations
- Deployment Architecture
Nzila is a production-grade, multi-tenant SaaS platform for gym management. Built with React 19, TypeScript, and Supabase, it provides 21 core modules serving gym owners, staff, trainers, and members.
| Metric | Value |
|---|---|
| Frontend Framework | React 19 + TypeScript 5.9 |
| Backend | Supabase (PostgreSQL 15) |
| Database Tables | 67 |
| RLS Policies | 100% coverage |
| Feature Modules | 21 |
| UI Components | 50+ (shadcn/ui) |
| TanStack Query Hooks | 19 |
| i18n Coverage | 95% (EN/PT) |
| Security Score | 7.8/10 |
┌──────────────────────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ React 19 Application │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Landing │ │ Auth │ │ Dashboard │ │ Member │ │ │
│ │ │ Page │ │ Module │ │ Layouts │ │ Portal │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 21 Feature Modules (Lazy Loaded) │ │ │
│ │ │ Members │ Calendar │ Training │ Payments │ Invoices │ Leads │ ... │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Shared Layer │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │ │
│ │ │ │ shadcn/ui │ │ TanStack │ │ React │ │ i18n │ │ │ │
│ │ │ │ Components │ │ Query │ │ Context │ │ (EN/PT) │ │ │ │
│ │ │ │ (50+) │ │ (19 hooks) │ │ (Auth/Gym) │ │ │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
│
│ HTTPS / WebSocket
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ API LAYER │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Supabase Edge Functions (Deno) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ auth-with- │ │ send-email │ │ create-user- │ │ gdpr-export │ │ │
│ │ │ rate-limit │ │ (6 templates)│ │ account │ │ gdpr-delete │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Shared Utilities │ │ │
│ │ │ cors.ts │ sanitize.ts │ validation.ts │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
│
│ PostgreSQL Wire Protocol
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ DATABASE LAYER │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ PostgreSQL 15 (Supabase) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ 67 Tables │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │ gyms │ │ members │ │ classes │ │payments │ │ invoices│ ... │ │ │
│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Row-Level Security (RLS) │ │ │
│ │ │ 100% Table Coverage │ gym_id Isolation │ Role-Based Access │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Database Functions & Triggers │ │ │
│ │ │ update_updated_at │ audit_log_trigger │ get_user_gym_id │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
│ │ Supabase Auth │ │ Supabase Storage │ │
│ │ - JWT Sessions │ │ - Payment Proofs │ │
│ │ - Rate Limiting │ │ - Member Photos │ │
│ │ - Password Policy │ │ - Gym Logos │ │
│ └──────────────────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
App.tsx
├── BrowserRouter
│ ├── QueryClientProvider (TanStack Query)
│ │ ├── ThemeProvider (next-themes)
│ │ │ ├── TooltipProvider
│ │ │ │ ├── AuthProvider
│ │ │ │ │ ├── GymProvider
│ │ │ │ │ │ ├── Routes
│ │ │ │ │ │ │ ├── Public Routes
│ │ │ │ │ │ │ │ ├── "/" → Index (Landing)
│ │ │ │ │ │ │ │ ├── "/auth" → AuthPage
│ │ │ │ │ │ │ │ ├── "/privacy" → Privacy
│ │ │ │ │ │ │ │ └── "/terms" → Terms
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └── Protected Routes (ProtectedRoute wrapper)
│ │ │ │ │ │ │ ├── DashboardLayout
│ │ │ │ │ │ │ │ ├── "/dashboard" → Dashboard
│ │ │ │ │ │ │ │ ├── "/calendar" → Calendar
│ │ │ │ │ │ │ │ ├── "/members" → Members (lazy)
│ │ │ │ │ │ │ │ ├── "/check-ins" → CheckIns
│ │ │ │ │ │ │ │ ├── "/disciplines" → Disciplines
│ │ │ │ │ │ │ │ ├── "/training" → Training (lazy)
│ │ │ │ │ │ │ │ ├── "/payments" → Payments (lazy)
│ │ │ │ │ │ │ │ ├── "/staff" → Staff (lazy)
│ │ │ │ │ │ │ │ ├── "/trainers" → Trainers (lazy)
│ │ │ │ │ │ │ │ ├── "/settings/*" → Settings (lazy)
│ │ │ │ │ │ │ │ └── "/superadmin/*" → SuperAdmin (lazy)
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └── Member Portal
│ │ │ │ │ │ │ ├── "/member/portal" → MemberPortal
│ │ │ │ │ │ │ ├── "/member/activity" → MemberActivity
│ │ │ │ │ │ │ ├── "/member/check-in" → MemberCheckIn
│ │ │ │ │ │ │ └── "/member/finances" → MemberFinances
src/
├── components/ # React components
│ ├── ui/ # shadcn/ui base (50+ components)
│ ├── layout/ # DashboardLayout, navigation
│ ├── common/ # Shared (ErrorBoundary, PageHeader)
│ ├── auth/ # Auth components
│ ├── dashboard/ # Dashboard widgets
│ ├── calendar/ # Calendar, RecurringClassForm
│ ├── member/ # Member management, elite portal
│ ├── payments/ # Payment components
│ ├── training/ # Training hub components
│ ├── settings/ # Settings panels
│ ├── notifications/ # Alert components
│ └── landing/ # Landing page components
├── contexts/ # React Context providers
│ ├── AuthContext.tsx
│ └── GymContext.tsx
├── hooks/ # Custom hooks
│ ├── useMembersData.tanstack.tsx
│ ├── usePaymentsData.tanstack.tsx
│ ├── useCalendarData.tanstack.tsx
│ ├── useCheckInsData.tanstack.tsx
│ ├── useDisciplinesData.tanstack.tsx
│ ├── useWorkoutsData.tanstack.tsx
│ ├── useInvoicesData.tanstack.tsx
│ ├── useFinancialData.tanstack.tsx
│ ├── useMemberProgressData.tanstack.tsx
│ ├── useExercisesData.tanstack.tsx
│ ├── useWorkoutBlocksData.tanstack.tsx
│ ├── useRBAC.ts
│ ├── useRealtimeUpdates.ts
│ ├── useFormWithZod.ts
│ ├── usePWA.ts
│ └── use-mobile.tsx
├── modules/ # Feature modules (21)
│ ├── auth/ # Authentication
│ ├── members/ # Member management
│ ├── payments/ # Payments & reconciliation
│ ├── calendar/ # Calendar & scheduling
│ ├── booking/ # Class booking
│ ├── invoices/ # Invoice management
│ ├── leads/ # CRM leads
│ ├── pos/ # Point of sale
│ ├── inventory/ # Inventory management
│ ├── trainers/ # Trainer management
│ ├── staff/ # Staff management
│ ├── training/ # Training hub
│ ├── checkins/ # Check-in system
│ ├── kiosk/ # Kiosk mode
│ ├── gdpr/ # GDPR compliance
│ ├── reporting/ # Financial reporting
│ ├── notifications/ # Email services
│ ├── events/ # Event bus
│ ├── onboarding/ # Setup wizard
│ ├── superadmin/ # Super admin
│ ├── saas-admin/ # SaaS administration
│ └── settings/ # Settings module
├── pages/ # Route pages
├── lib/ # Utilities
├── types/ # TypeScript types
├── integrations/ # External integrations
│ └── supabase/
├── i18n/ # Internationalization
│ ├── index.ts
│ └── locales/
│ ├── en/ # English translations
│ └── pt-pt/ # Portuguese translations
└── utils/ # Utility functions
┌──────────────────────────────────────────────────────────────────────────────────┐
│ STATE MANAGEMENT LAYERS │
└──────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────┐
│ LAYER 1: SERVER STATE │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ TanStack Query (19 Hooks) │ │
│ │ │ │
│ │ Data Fetching Hooks: │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ useMembersData │ │ usePaymentsData │ │ useCalendarData │ │ │
│ │ │ - useQuery │ │ - useQuery │ │ - useQuery │ │ │
│ │ │ - useMutation │ │ - useMutation │ │ - useMutation │ │ │
│ │ │ - Optimistic │ │ - Optimistic │ │ - Optimistic │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ useCheckInsData │ │ useDisciplines │ │ useWorkoutsData │ │ │
│ │ │ │ │ Data │ │ │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ │ │ │
│ │ Cache Strategy: │ │
│ │ - staleTime: 5 minutes (configurable) │ │
│ │ - gcTime: 30 minutes │ │
│ │ - refetchOnWindowFocus: true │ │
│ │ - Automatic cache invalidation on mutations │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────┐
│ LAYER 2: CLIENT STATE │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ React Context Providers │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ AuthContext │ │ │
│ │ │ State: Actions: │ │ │
│ │ │ - user: User | null - login(email, password) │ │ │
│ │ │ - session: Session - logout() │ │ │
│ │ │ - isLoading: boolean - signUp(email, password, metadata) │ │ │
│ │ │ - role: UserRole - resetPassword(email) │ │ │
│ │ │ - profile: Profile - updateProfile(data) │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ GymContext │ │ │
│ │ │ State: Actions: │ │ │
│ │ │ - gym: Gym | null - setCurrentGym(gymId) │ │ │
│ │ │ - gymId: string - refreshGymData() │ │ │
│ │ │ - settings: GymSettings - updateGymSettings(data) │ │ │
│ │ │ - locations: Location[] │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ SecureAuthContext │ │ │
│ │ │ Enhanced security features: │ │ │
│ │ │ - Session timeout warnings │ │ │
│ │ │ - Automatic session refresh │ │ │
│ │ │ - Inactivity detection │ │ │
│ │ │ - Force logout on security events │ │ │
│ │ └──────────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────┐
│ LAYER 3: UI STATE │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Component-Local State │ │
│ │ - useState for form inputs │ │
│ │ - useReducer for complex forms │ │
│ │ - React Hook Form for form management │ │
│ │ - URL state (React Router) for filters/pagination │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────┐
│ EDGE FUNCTION LAYER │
└──────────────────────────────────────────────────────────────────────────────────┘
┌───────────────────┐
│ HTTP Request │
└─────────┬─────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ SHARED MIDDLEWARE │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ cors.ts │ │
│ │ - Origin validation against whitelist │ │
│ │ - CORS headers generation │ │
│ │ - Preflight request handling │ │
│ │ │ │
│ │ const ALLOWED_ORIGINS = [ │ │
│ │ /^https:\/\/.*\.lovable\.app$/, │ │
│ │ /^https:\/\/.*\.supabase\.co$/, │ │
│ │ "https://nzila.app", │ │
│ │ "http://localhost:5173" │ │
│ │ ]; │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ sanitize.ts │ │
│ │ - encodeHTMLEntities(): XSS prevention │ │
│ │ - sanitizeURL(): URL validation and sanitization │ │
│ │ - stripHTML(): Remove HTML tags │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ validation.ts │ │
│ │ - Email format validation │ │
│ │ - Input length limits │ │
│ │ - Type checking utilities │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ EDGE FUNCTIONS │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ auth-with-rate-limit │ │
│ │ Purpose: Rate-limited authentication │ │
│ │ Rate Limit: 5 attempts per 15 minutes per IP │ │
│ │ Lockout: 30 minutes after limit exceeded │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ send-email │ │
│ │ Purpose: Transactional email via Resend API │ │
│ │ Auth: JWT required for custom emails │ │
│ │ Templates: welcome_self_signup, welcome_admin_created, password_reset │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ create-user-account │ │
│ │ Purpose: Admin creates user accounts │ │
│ │ Auth: Admin JWT required │ │
│ │ Creates: auth.users entry + profiles entry + member entry │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ pre-register-gym-owner │ │
│ │ Purpose: Super admin pre-registers gym owners │ │
│ │ Auth: Super admin JWT required │ │
│ │ Creates: gym_owner_invitations entry │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ TIER 1: DISCIPLINES MODULE │
│ │
│ Purpose: Activity Catalog & Taxonomy | Propósito: Catálogo de Atividades │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Activity Types │ │ Rank Systems │ │ Category │ │
│ │ - BJJ │ │ - Belt Levels │ │ Taxonomy │ │
│ │ - Yoga │ │ - Stripe System │ │ - Combat │ │
│ │ - CrossFit │ │ - Criteria │ │ - Wellness │ │
│ │ - Pilates │ │ - Requirements │ │ - Strength │ │
│ │ - Boxing │ │ - Colors │ │ - Cardio │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ Tables: disciplines, discipline_ranks │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ FK: discipline_id
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ TIER 2: CALENDAR MODULE │
│ │
│ Purpose: Scheduling Engine | Propósito: Motor de Agendamento │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Class Schedule │ │ Recurring │ │ Booking System │ │
│ │ - Visual Cal │ │ Series │ │ - Reservations │ │
│ │ - Time Slots │ │ - RRULE Engine │ │ - Waitlist FIFO │ │
│ │ - Coach Assign │ │ - Exceptions │ │ - Cancellations │ │
│ │ - Capacity │ │ - Auto-generate │ │ - Promotions │ │
│ │ - Locations │ │ - End Dates │ │ - Check-ins │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ Tables: classes, class_series, class_bookings, class_types, locations │
└─────────────────────────────────────────────────────────────────────────────┘
│
│ FK: workout_template_id
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ TIER 3: TRAINING MODULE │
│ │
│ Purpose: Member Programming | Propósito: Programação de Membros │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Exercise │ │ Workout │ │ Progress │ │
│ │ Library │ │ Templates │ │ Tracking │ │
│ │ - 14 Categories │ │ - Block Builder │ │ - Performance │ │
│ │ - Equipment │ │ - Polymorphic │ │ - Promotions │ │
│ │ - Muscle Groups │ │ - WOD Types │ │ - Rank History │ │
│ │ - Difficulty │ │ - Assignments │ │ - Achievements │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ Tables: gym_exercises, workout_templates, workout_blocks, exercise_instances│
│ member_ranks, performance_records │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────────┐
│ CORE ENTITIES │
└──────────────────────────────────────────────────────────────────────────────────┘
┌─────────────┐ ┌─────────────┐
│ gyms │ ◄────────────────►│ profiles │
│ │ 1:N │ │
│ - id (PK) │ │ - id (PK) │
│ - name │ │ - user_id │
│ - slug │ │ - gym_id │
│ - settings │ │ - role │
└──────┬──────┘ └─────────────┘
│
│ 1:N (gym_id FK on all tables)
│
┌──────┴────────────────────────────────────────────────────────────────┐
│ │
▼ ▼ ▼ ▼ │
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ members │ │ classes │ │disciplines│ │ payments│ │
│ │◄──────►│ │◄──────►│ │ │ │ │
│ │ N:M │ │ 1:N │ │ │ │ │
└────┬────┘ └────┬────┘ └────┬─────┘ └────┬────┘ │
│ │ │ │ │
▼ ▼ ▼ ▼ │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ check_ins│ │class_ │ │discipline│ │ invoices │ │
│ │ │bookings │ │_ranks │ │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└───────────────────────────────────────────────────────────────────────────┘
| Category | Tables | Description |
|---|---|---|
| Core | 8 | gyms, profiles, members, locations, settings |
| Scheduling | 6 | classes, class_series, class_bookings, class_types |
| Training | 8 | disciplines, discipline_ranks, gym_exercises, workout_templates |
| Finance | 6 | payments, invoices, invoice_items, discounts, bank_reconciliations |
| Operations | 8 | check_ins, leads, lead_tasks, trainers, staff, assets |
| Auth & Security | 10 | auth_events, auth_rate_limits, audit_logs, gdpr_consents |
| Platform | 8 | gym_subscriptions, platform_plans, feature_flags, support_tickets |
| Communication | 4 | email_notifications, whatsapp_messages |
| GDPR | 3 | gdpr_consents, data_export_requests, deletion_requests |
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 1: FRONTEND SECURITY │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Input │ │ Output │ │ Route │ │
│ │ Validation │ │ Sanitization │ │ Protection │ │
│ │ - Zod schemas │ │ - HTML encoding │ │ - ProtectedRoute│ │
│ │ - Type guards │ │ - XSS prevention│ │ - RequirePermit │ │
│ │ - Length limits │ │ - URL sanitize │ │ - RBAC hooks │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 2: API SECURITY │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Authentication │ │ Rate Limiting │ │ CORS │ │
│ │ - JWT tokens │ │ - Per IP/user │ │ - Origin check │ │
│ │ - Session mgmt │ │ - Exponential │ │ - Whitelist │ │
│ │ - Timeout warn │ │ backoff │ │ - Credentials │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 3: DATABASE SECURITY │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Row-Level │ │ Multi-Tenant │ │ Sensitive Data │ │
│ │ Security │ │ Isolation │ │ Protection │ │
│ │ - 67 tables │ │ - gym_id FK │ │ - member_ │ │
│ │ - CRUD policies │ │ - Context funcs │ │ sensitive_data│ │
│ │ - Role checks │ │ - No cross-read │ │ - Encryption │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYER 4: AUDIT & MONITORING │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Audit Logs │ │ Auth Events │ │ Error Tracking │ │
│ │ - Immutable │ │ - Login/logout │ │ - correlationId │ │
│ │ - Trigger-based │ │ - Failed auth │ │ - Safe payloads │ │
│ │ - Entity track │ │ - Session mgmt │ │ - Stack hidden │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Gym A Tenant Gym B Tenant
──────────── ────────────
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ gym_id: A │ │ gym_id: B │
│ │ │ │
│ ┌─────────┐ │ ISOLATION │ ┌─────────┐ │
│ │ members │ │ ◄─────────────────────────────► │ │ members │ │
│ └─────────┘ │ (No cross-read) │ └─────────┘ │
│ ┌─────────┐ │ │ ┌─────────┐ │
│ │ classes │ │ │ │ classes │ │
│ └─────────┘ │ │ └─────────┘ │
│ ┌─────────┐ │ │ ┌─────────┐ │
│ │payments │ │ │ │payments │ │
│ └─────────┘ │ │ └─────────┘ │
└─────────────┘ └─────────────┘
RLS POLICY PATTERN:
CREATE POLICY "Users can only view their gym data"
ON public.members
FOR SELECT
USING (
gym_id = (
SELECT gym_id FROM public.profiles
WHERE user_id = auth.uid()
)
);
┌─────────────────┬───────────┬───────┬─────────┬───────┬────────┐
│ Permission │Super Admin│ Owner │ Manager │ Admin │ Member │
├─────────────────┼───────────┼───────┼─────────┼───────┼────────┤
│ members:read │ ✓ │ ✓ │ ✓ │ ✓ │ Own │
│ members:write │ ✓ │ ✓ │ ✓ │ ✓ │ Own │
│ payments:read │ ✓ │ ✓ │ ✓ │ ✓ │ Own │
│ payments:write │ ✓ │ ✓ │ ✓ │ ─ │ ─ │
│ settings:read │ ✓ │ ✓ │ ─ │ ─ │ ─ │
│ settings:write │ ✓ │ ✓ │ ─ │ ─ │ ─ │
│ platform:manage │ ✓ │ ─ │ ─ │ ─ │ ─ │
└─────────────────┴───────────┴───────┴─────────┴───────┴────────┘
1. Event Bus (Loose Coupling)
─────────────────────────────
// src/modules/events/eventBus.ts
const eventBus = new EventEmitter();
// Publishing
eventBus.emit('booking:created', { bookingId, memberId, classId });
// Subscribing
eventBus.on('booking:created', async (event) => {
await sendBookingConfirmation(event);
await updateMemberStats(event);
});
2. TanStack Query Invalidation (Data Sync)
──────────────────────────────────────────
// After creating a payment
useMutation({
mutationFn: createPayment,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payments'] });
queryClient.invalidateQueries({ queryKey: ['invoices'] });
queryClient.invalidateQueries({ queryKey: ['member', memberId] });
}
});
3. Database Triggers (Backend Sync)
───────────────────────────────────
-- Automatic audit logging
CREATE TRIGGER audit_members_changes
AFTER INSERT OR UPDATE OR DELETE ON members
FOR EACH ROW EXECUTE FUNCTION log_audit_event();
-- Automatic timestamp updates
CREATE TRIGGER update_members_timestamp
BEFORE UPDATE ON members
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
┌─────────────────────────────────────────────────────────────────┐
│ RESEND (Email) │
│ Integration Point: supabase/functions/send-email │
│ Auth: RESEND_API_KEY environment variable │
│ Flow: App → Edge Function → Resend API → Recipient │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SUPABASE STORAGE (Files) │
│ Integration Point: Direct Supabase client │
│ Buckets: payment-proofs, member-photos, gym-logos, documents │
│ Security: RLS on storage.objects table │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SUPABASE REALTIME (Live Updates) │
│ Integration Point: Supabase client subscriptions │
│ Enabled Tables: check_ins, class_bookings, notifications │
│ Hook: useRealtimeUpdates() │
└─────────────────────────────────────────────────────────────────┘
Layer 1: TanStack Query (Client-side)
─────────────────────────────────────
- staleTime: 5 minutes (user-specific data)
- staleTime: 30 minutes (reference data like disciplines)
- gcTime: 30 minutes
- Automatic background refetching
- Optimistic updates for mutations
Layer 2: Code Splitting
───────────────────────
- Route-based lazy loading for all 21 modules
- Critical path: main + vendor (~450KB)
- Module chunks loaded on navigation
- Prefetch common modules after initial load
┌────────────────────────────────────────────────────────────────┐
│ main.js │ ~150KB │ Core React, Router, Context │
├────────────────────────────────────────────────────────────────┤
│ vendor.js │ ~300KB │ TanStack Query, Supabase, UI │
├────────────────────────────────────────────────────────────────┤
│ members.chunk.js │ ~50KB │ Members module (lazy) │
│ payments.chunk.js │ ~40KB │ Payments module (lazy) │
│ calendar.chunk.js │ ~60KB │ Calendar module (lazy) │
│ training.chunk.js │ ~45KB │ Training module (lazy) │
│ ... │ │ Other modules (lazy) │
└────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ LOVABLE CLOUD │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Frontend (Vite) │ │
│ │ Build: npm run build / bun run build │ │
│ │ Output: dist/ │ │
│ │ CDN: Global edge distribution │ │
│ │ URL: https://project.lovable.app │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Supabase Project │ │
│ │ Database: PostgreSQL 15 │ │
│ │ Auth: Supabase Auth (JWT) │ │
│ │ Storage: Supabase Storage │ │
│ │ Edge Functions: Deno runtime │ │
│ │ Realtime: WebSocket subscriptions │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Environment Variables:
VITE_SUPABASE_URL=https://project.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=eyJ...
VITE_SUPABASE_PROJECT_ID=project-id
# Edge function secrets (managed in Lovable Cloud)
RESEND_API_KEY=re_...
FROM_EMAIL=noreply@gym.com
SITE_URL=https://project.lovable.app
WEBHOOK_SECRET=whsec_...
- README.md - Project overview
- DATABASE.md - Complete database schema
- SECURITY.md - Security policy
- TODO.md - Production backlog
- ROADMAP.md - Feature roadmap
Version: 1.0.5 | Last Updated: February 8, 2026