# System Architecture — SaaS Job Portal Platform

## 1. Overview

A multi-tenant SaaS Job Portal built on **CodeIgniter 4** (PHP 8.3+), **MySQL 8+**, with **Redis** as an optional cache/queue layer. The platform serves four primary user types (Super Admin, Agency Owner, Recruiter, Candidate) plus optional Support Staff, isolated per-tenant (Agency) at the data layer.

## 2. Architectural Style

- **Pattern:** Layered MVC + Repository + Service Layer (not fat controllers/models)
- **Multi-tenancy model:** Single database, shared schema, `agency_id` discriminator column on all tenant-scoped tables (row-level isolation). Chosen over database-per-tenant for operational simplicity at SaaS-startup scale; can be upgraded to schema-per-tenant later without touching business logic (isolation is enforced in the Repository layer, not scattered in controllers).
- **API style:** REST, versioned (`/api/v1/...`), stateless, token-based (JWT + refresh tokens).

## 3. Layers

```
┌─────────────────────────────────────────────┐
│  Presentation Layer                          │
│  - Web (Bootstrap5 + AdminLTE + jQuery/AJAX) │
│  - REST API consumers (mobile/3rd party)     │
├─────────────────────────────────────────────┤
│  Controller Layer (CI4 Controllers)          │
│  - Request validation, auth check, response  │
├─────────────────────────────────────────────┤
│  Service Layer                               │
│  - Business rules, orchestration,            │
│    transactions, cross-module workflows      │
├─────────────────────────────────────────────┤
│  Repository Layer                            │
│  - Query building, tenant scoping,           │
│    caching, DB abstraction                   │
├─────────────────────────────────────────────┤
│  Model Layer (CI4 Models / Entities)         │
│  - Table mapping, casts, validation rules    │
├─────────────────────────────────────────────┤
│  Infrastructure                              │
│  - MySQL 8, Redis (cache/queue/session),     │
│    Storage (local/S3), Mail (SMTP),          │
│    Payment gateways, WebSocket (chat)        │
└─────────────────────────────────────────────┘
```

### Directory structure (CI4 app/)

```
app/
├── Controllers/
│   ├── Admin/
│   ├── Agency/
│   ├── Recruiter/
│   ├── Candidate/
│   ├── Api/V1/
│   └── Install/
├── Models/
├── Entities/
├── Repositories/
│   ├── Contracts/          (interfaces)
│   └── Eloquent-style CI4 Repos
├── Services/
│   ├── Auth/
│   ├── Subscription/
│   ├── Credit/
│   ├── Wallet/
│   ├── Payment/
│   ├── Matching/
│   ├── Notification/
│   ├── Reporting/
│   └── Audit/
├── Filters/                 (middleware: TenantScope, RoleGuard, RateLimit, ApiAuth)
├── Libraries/
│   ├── PaymentGateways/     (Stripe, PayPal, JazzCash, Easypaisa, BankTransfer adapters)
│   ├── NotificationChannels/ (Email, SMS, InApp, WhatsApp)
│   └── PdfExporter/, ExcelExporter/
├── Validation/
├── Database/
│   ├── Migrations/
│   └── Seeds/
└── Config/
```

## 4. Multi-Tenant Isolation Strategy

1. Every tenant-scoped table carries `agency_id` (nullable only for platform-level tables).
2. A `TenantScope` filter runs on every authenticated request, resolving the current agency from the logged-in user/token and injecting it into a request-scoped `TenantContext` singleton.
3. All Repositories extend `BaseRepository`, which **automatically applies `WHERE agency_id = ?`** unless explicitly run in `withoutTenantScope()` (reserved for Super Admin queries only, itself permission-gated).
4. File storage is namespaced: `storage/agencies/{agency_id}/...`.
5. Redis cache keys are prefixed `tenant:{agency_id}:...`.

This guarantees isolation is enforced structurally (one place), not per-query by convention — eliminating the most common SaaS data-leak bug class.

## 5. Request Lifecycle (Web)

```
Browser → Router → Auth Filter → Role Guard Filter → Tenant Scope Filter
   → Rate Limit Filter → Controller → Service → Repository → Model → MySQL
   ← Response (view/AJAX JSON) ←────────────────────────────────────────
```

## 6. Request Lifecycle (API)

```
Client → /api/v1/* → ApiAuth Filter (JWT verify) → Tenant Scope Filter
   → Rate Limit Filter (per-token, Redis counter) → Api Controller
   → Service → Repository → Model → MySQL
   ← JSON:API-style response (data/meta/errors) ←──────────────────────
```

## 7. Background Processing

- CI4 `spark` commands executed via **system cron** (not in-request) for: subscription expiry, credit expiry, notification digests, interview reminders, cleanup, report generation.
- Optional Redis-backed queue (`Services/Queue`) for async jobs: email sending, PDF/Excel generation, resume parsing — falls back to synchronous execution if Redis is unavailable (so Redis stays truly optional per requirements).

## 8. Key Cross-Cutting Concerns

| Concern | Implementation |
|---|---|
| Auth | Session (web) + JWT/refresh token (API), Argon2id password hashing |
| Authorization | Role + Permission matrix (see doc 04), enforced via `RoleGuard` filter + service-layer checks |
| Validation | CI4 Validation rules per request class, server-side only source of truth |
| Audit Logging | `AuditService` wraps all write operations on sensitive entities (before/after diff) |
| Activity Logging | `ActivityLogger` middleware logs every tracked action (see doc 01 §Activity Tracking) |
| Rate Limiting | Redis token-bucket per user/IP/API-key; DB fallback (`throttle` table) if Redis absent |
| Caching | Redis for: session, plan limits, search facets, dashboard aggregates (optional, degrades gracefully) |
| i18n | CI4 Language files, ready for `en` + `ur` |

## 9. Non-Functional Targets

- Horizontal scalability: stateless app servers behind load balancer, sessions in Redis/DB (not file).
- DB: InnoDB, proper indexing (see schema doc), read-heavy search endpoints candidate for read-replica later.
- Uptime target: designed for zero-downtime deploys (migrations backward-compatible, feature-flagged rollouts).
