# 🗄️ Database Architecture - GoHorseJobs GoHorseJobs uses a single **PostgreSQL 16+** database shared across all services (Backend API, NestJS Backoffice, Node.js Seeder). --- ## 🏗️ Core Entity-Relationship Diagram (ERD) The core data model centers around `users` acting in different capacities (`candidate`, `recruiter`, `admin`) interacting with `companies` and `jobs`. ```mermaid erDiagram USERS ||--o{ USER_COMPANIES : "belongs to" COMPANIES ||--o{ USER_COMPANIES : "contains" COMPANIES ||--o{ JOBS : "posts" USERS ||--o{ JOBS : "creates (author)" USERS ||--o{ APPLICATIONS : "submits" JOBS ||--o{ APPLICATIONS : "receives" USERS ||--o{ FAVORITE_JOBS : "saves" JOBS ||--o{ FAVORITE_JOBS : "favorited" USERS ||--o{ TICKETS : "opens" USERS ||--o{ TICKET_MESSAGES : "sends" TICKETS ||--o{ TICKET_MESSAGES : "has" USERS { uuid id PK string role "superadmin, admin, recruiter, candidate" string identifier "username or email" string email UK string password_hash boolean is_active timestamp created_at } COMPANIES { uuid id PK string name string document "CNPJ" string domain UK string location string industry string logo_url string status "pending, active, rejected" } USER_COMPANIES { uuid id PK uuid user_id FK uuid company_id FK string role "owner, admin, member" } JOBS { uuid id PK uuid company_id FK uuid author_id FK string title text description string type "full-time, remote, etc" decimal max_salary decimal min_salary string status "draft, published, closed" } APPLICATIONS { uuid id PK uuid job_id FK uuid candidate_id FK string status "pending, reviewing, interviewed, rejected, accepted" text cover_letter } FAVORITE_JOBS { uuid id PK uuid user_id FK uuid job_id FK timestamp created_at } TICKETS { uuid id PK uuid user_id FK "nullable for guests" string subject string category "Support, Bug, Billing..." string status "open, in_progress, resolved, closed" string email "fallback for guests" string name "fallback for guests" } ``` --- ## 🔑 Key Strategies ### 1. Primary Keys (UUID v7) We aggressively use **UUID v7** for all major business entities (`users`, `companies`, `jobs`). * **Why?** UUID v7 contains a timestamp component, making it naturally sortable by insertion time (like SERIAL/auto-increment) while avoiding the predictability and distributed generation bottlenecks of standard integers. ### 2. Multi-Role Profiles The system handles permissions through the `role` enum in the `users` table: * `superadmin`: Global control over GoHorseJobs. * `admin`: Can moderate companies/jobs inside the Backoffice. * `recruiter`: A user attached to a `company_id` via `user_companies` who can post `jobs`. * `candidate`: A standard user seeking jobs and making `applications`. A single email can only correspond to one role globally. If a user needs another role, they must change it or use a different credential. ### 3. Migrations Migrations are written in pure SQL and stored in `backend/migrations/`. They follow a strict sequential numbering index (e.g., `000_schema.sql`, `001_roles.sql`, etc). The backend runtime executes these on startup if the database is out of sync. ### 4. Background Seeding Seeding (populating test data) is explicitly decoupled from migrations. It lives isolated in the Node.js `seeder-api`. To reset your local database to a clean, populated state: ```bash # Deletes everything, remigrates, and inserts base data (except 153k cities) cd seeder-api && npm run seed:lite ``` --- ## ⚠️ Security Notes ### The `password_hash` & Pepper Bcrypt hashes stored in the standard `users` table require a secret pepper (`PASSWORD_PEPPER`). * The raw hash stored in the DB cannot be cracked immediately due to the pepper. * The seeder relies on reading the `PASSWORD_PEPPER` from `.env` to forge the initial super-admin and test accounts hashes. **If you change the `.env` pepper, the entire DB of seeded passwords immediately invalidates.** You must re-run `npm run seed`.