# πŸ—„οΈ Database Schema Documentation Complete database documentation for the GoHorseJobs platform. > **Last Updated:** 2024-12-24 > **Database:** PostgreSQL 15+ > **ID Strategy:** SERIAL (INT) for core tables, UUID v7 for newer tables --- ## πŸ“Š Entity Relationship Diagram ```mermaid erDiagram %% Core Entities users ||--o{ user_companies : "belongs to" users ||--o{ user_roles : "has roles" users ||--o{ applications : "submits" users ||--o{ favorite_jobs : "saves" users ||--o{ notifications : "receives" companies ||--o{ user_companies : "has members" companies ||--o{ jobs : "posts" jobs ||--o{ applications : "receives" jobs ||--o{ favorite_jobs : "saved by" jobs ||--o{ job_payments : "has payments" regions ||--o{ cities : "contains" regions ||--o{ companies : "located in" regions ||--o{ jobs : "located in" %% Entities users { int id PK "SERIAL" varchar identifier UK "login" varchar password_hash varchar role "enum" varchar full_name varchar email varchar name int tenant_id FK "nullable" varchar status } user_roles { int user_id PK,FK varchar role PK "composite key" } companies { int id PK "SERIAL" varchar name varchar slug UK varchar type varchar document text address int region_id FK varchar email varchar website boolean verified boolean active } jobs { int id PK "SERIAL" int company_id FK int created_by FK varchar title text description decimal salary_min decimal salary_max varchar salary_type varchar currency varchar employment_type varchar work_mode varchar status boolean is_featured } applications { int id PK "SERIAL" int job_id FK int user_id FK "nullable" varchar status text message varchar resume_url } regions { int id PK "SERIAL" varchar name varchar country_code varchar code } cities { int id PK "SERIAL" int region_id FK varchar name } notifications { uuid id PK int user_id FK varchar type varchar title text message boolean read } job_payments { uuid id PK int job_id FK int user_id FK decimal amount varchar status varchar stripe_session_id } ``` --- ## πŸ—οΈ Table Overview | Table | ID Type | Description | Migration | |-------|---------|-------------|-----------| | `users` | SERIAL | System users (candidates, recruiters, admins) | 001 | | `user_roles` | Composite | Additional roles per user | 020 | | `companies` | SERIAL | Employer organizations | 002 | | `user_companies` | SERIAL | User ↔ Company mapping | 003 | | `regions` | SERIAL | States/Provinces/Prefectures | 004 | | `cities` | SERIAL | Cities within regions | 004 | | `jobs` | SERIAL | Job postings | 005 | | `applications` | SERIAL | Job applications | 006 | | `favorite_jobs` | SERIAL | Saved jobs by users | 007 | | `password_resets` | SERIAL | Password reset tokens | 008 | | `notifications` | UUID v4 | User notifications | 016 | | `tickets` | UUID v4 | Support tickets | 017 | | `ticket_messages` | UUID v4 | Ticket messages | 017 | | `job_payments` | UUID v4 | Payment records | 019 | | `job_posting_prices` | SERIAL | Pricing configuration | 019 | | `login_audits` | SERIAL | Login audit trail | 013 | | `activity_logs` | SERIAL | Activity logging | 013 | --- ## πŸ“‹ Core Tables ### `users` System users including Candidates, Recruiters, and Admins. ```sql CREATE TABLE users ( id SERIAL PRIMARY KEY, identifier VARCHAR(100) UNIQUE NOT NULL, -- Login (username/email) password_hash VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL, -- 'superadmin'|'companyAdmin'|'recruiter'|'jobSeeker' -- Profile full_name VARCHAR(255), email VARCHAR(255), name VARCHAR(255), phone VARCHAR(30), -- Candidate Profile Fields (015) city VARCHAR(100), state VARCHAR(50), title VARCHAR(100), experience VARCHAR(100), skills TEXT[], bio TEXT, objective TEXT, -- Multi-tenancy (020) tenant_id INT REFERENCES companies(id), status VARCHAR(20) DEFAULT 'active', -- Settings language VARCHAR(5) DEFAULT 'en', active BOOLEAN DEFAULT true, -- Timestamps created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_login_at TIMESTAMP ); ``` **Roles:** - `superadmin` - Platform administrator - `companyAdmin` - Company administrator - `recruiter` - Job poster/recruiter - `jobSeeker` - Candidate/job seeker --- ### `user_roles` Additional roles per user (for multi-role support). ```sql CREATE TABLE user_roles ( user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, role VARCHAR(50) NOT NULL, PRIMARY KEY (user_id, role) ); ``` --- ### `companies` Employer organizations that post jobs. ```sql CREATE TABLE companies ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, slug VARCHAR(255) UNIQUE NOT NULL, -- URL-friendly name type VARCHAR(50) DEFAULT 'company', -- 'company'|'agency'|'system' document VARCHAR(100), -- CNPJ/Tax ID -- Location address TEXT, region_id INT REFERENCES regions(id), city_id INT REFERENCES cities(id), -- Contact phone VARCHAR(30), email VARCHAR(255), website VARCHAR(255), -- Branding logo_url TEXT, description TEXT, -- JSON or plain text -- Status active BOOLEAN DEFAULT true, verified BOOLEAN DEFAULT false, -- Timestamps created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ### `jobs` Job postings created by companies. ```sql CREATE TABLE jobs ( id SERIAL PRIMARY KEY, company_id INT NOT NULL REFERENCES companies(id) ON DELETE CASCADE, created_by INT NOT NULL REFERENCES users(id), -- Job Details title VARCHAR(255) NOT NULL, description TEXT NOT NULL, -- Salary salary_min DECIMAL(12,2), salary_max DECIMAL(12,2), salary_type VARCHAR(20), -- 'hourly'|'daily'|'weekly'|'monthly'|'yearly' currency VARCHAR(3), -- 'BRL'|'USD'|'EUR'|'GBP'|'JPY' -- Employment employment_type VARCHAR(30), -- 'full-time'|'part-time'|'contract'|'dispatch'|... work_mode VARCHAR(10), -- 'onsite'|'hybrid'|'remote' working_hours VARCHAR(100), -- Location location VARCHAR(255), region_id INT REFERENCES regions(id), city_id INT REFERENCES cities(id), -- Requirements & Benefits requirements JSONB, -- Array of skills/requirements benefits JSONB, -- Array of benefits -- Visa & Language visa_support BOOLEAN DEFAULT false, language_level VARCHAR(20), -- 'N5'|'N4'|'N3'|'N2'|'N1'|'beginner'|'none' -- Status status VARCHAR(20) DEFAULT 'open', -- 'draft'|'open'|'closed'|'published'|... is_featured BOOLEAN DEFAULT false, -- Timestamps created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` **Status Values:** - `draft` - Not published yet - `open` - Accepting applications - `published` - Live and visible - `paused` - Temporarily hidden - `closed` - No longer accepting - `expired` - Past expiration date - `archived` - Archived by employer - `reported` - Flagged for review - `review` - Under admin review --- ### `applications` Job applications from candidates. ```sql CREATE TABLE applications ( id SERIAL PRIMARY KEY, job_id INT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE, user_id INT REFERENCES users(id), -- Nullable for guest applications -- Applicant Info name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, phone VARCHAR(30), line_id VARCHAR(100), whatsapp VARCHAR(30), -- Application Content message TEXT, resume_url VARCHAR(500), documents JSONB, -- Status status VARCHAR(20) DEFAULT 'pending', -- 'pending'|'reviewed'|'shortlisted'|'rejected'|'hired' notes TEXT, -- Recruiter notes -- Timestamps created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## 🌍 Geographic Tables ### `regions` States, provinces, or prefectures. ```sql CREATE TABLE regions ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, country_code VARCHAR(2) NOT NULL, -- 'BR'|'US'|'JP' code VARCHAR(10) NOT NULL -- 'SP'|'CA'|'13' ); ``` **Seeded Regions:** - πŸ‡§πŸ‡· Brazil: SΓ£o Paulo (SP), Rio de Janeiro (RJ), Minas Gerais (MG) - πŸ‡ΊπŸ‡Έ USA: California (CA), New York (NY), Texas (TX) - πŸ‡―πŸ‡΅ Japan: Tokyo (13), Osaka (27) --- ### `cities` Cities within regions. ```sql CREATE TABLE cities ( id SERIAL PRIMARY KEY, region_id INT NOT NULL REFERENCES regions(id), name VARCHAR(100) NOT NULL ); ``` --- ## πŸ’° Payment Tables ### `job_payments` (UUID) Payment records for job postings. ```sql CREATE TABLE job_payments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), job_id INT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE, user_id INT REFERENCES users(id) ON DELETE SET NULL, -- Stripe stripe_session_id VARCHAR(255), stripe_payment_intent VARCHAR(255), stripe_customer_id VARCHAR(255), -- Amount amount DECIMAL(12,2) NOT NULL, currency VARCHAR(3) DEFAULT 'USD', -- Status status VARCHAR(20) DEFAULT 'pending', -- 'pending'|'completed'|'failed'|'refunded'|'expired' -- Billing billing_type VARCHAR(20), -- 'company'|'individual' billing_name VARCHAR(255), billing_email VARCHAR(255), -- Job Details duration_days INT DEFAULT 30, is_featured BOOLEAN DEFAULT false, -- Timestamps created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, paid_at TIMESTAMP, expires_at TIMESTAMP ); ``` --- ### `job_posting_prices` Pricing configuration for job postings. ```sql CREATE TABLE job_posting_prices ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, description TEXT, price DECIMAL(12,2) NOT NULL, currency VARCHAR(3) DEFAULT 'USD', duration_days INT DEFAULT 30, is_featured BOOLEAN DEFAULT false, stripe_price_id VARCHAR(255), active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## πŸ”” Notification Tables ### `notifications` (UUID) User notifications. ```sql CREATE TABLE notifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, type VARCHAR(50), title VARCHAR(255), message TEXT, read BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## 🎫 Support Tables ### `tickets` (UUID) Support tickets. ```sql CREATE TABLE tickets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id INT NOT NULL, subject VARCHAR(255) NOT NULL, status VARCHAR(20) DEFAULT 'open', -- 'open'|'in_progress'|'resolved'|'closed' priority VARCHAR(10) DEFAULT 'medium', -- 'low'|'medium'|'high'|'urgent' created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### `ticket_messages` (UUID) Messages within tickets. ```sql CREATE TABLE ticket_messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), ticket_id UUID NOT NULL REFERENCES tickets(id) ON DELETE CASCADE, user_id INT NOT NULL, message TEXT NOT NULL, is_staff BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## πŸ“ Audit Tables ### `login_audits` Login attempt tracking. ```sql CREATE TABLE login_audits ( id SERIAL PRIMARY KEY, user_id INT, identifier VARCHAR(100), success BOOLEAN, ip_address VARCHAR(45), user_agent TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### `activity_logs` General activity logging. ```sql CREATE TABLE activity_logs ( id SERIAL PRIMARY KEY, user_id INT, entity_type VARCHAR(50), entity_id INT, action VARCHAR(50), details JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## πŸ”— Junction Tables ### `user_companies` Maps users to companies (N:M relationship). ```sql CREATE TABLE user_companies ( id SERIAL PRIMARY KEY, user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, company_id INT NOT NULL REFERENCES companies(id) ON DELETE CASCADE, role VARCHAR(30) DEFAULT 'recruiter', -- 'companyAdmin'|'recruiter' permissions JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, company_id) ); ``` ### `favorite_jobs` Saved jobs by users. ```sql CREATE TABLE favorite_jobs ( id SERIAL PRIMARY KEY, user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, job_id INT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, job_id) ); ``` --- ## πŸ”„ Migrations History | # | File | Description | |---|------|-------------| | 001 | `001_create_users_table.sql` | Core users table | | 002 | `002_create_companies_table.sql` | Companies/employers | | 003 | `003_create_user_companies_table.sql` | User ↔ Company mapping | | 004 | `004_create_prefectures_cities_tables.sql` | Geographic data | | 005 | `005_create_jobs_table.sql` | Job postings | | 006 | `006_create_applications_table.sql` | Job applications | | 007 | `007_create_favorite_jobs_table.sql` | Saved jobs | | 008 | `008_create_password_resets_table.sql` | Password reset tokens | | 010 | `010_seed_super_admin.sql` | Default superadmin | | 011 | `011_add_is_featured_to_jobs.sql` | Featured jobs flag | | 012 | `012_add_work_mode.sql` | Work mode column | | 013 | `013_create_backoffice_tables.sql` | Audit tables | | 014 | `014_update_job_status_constraint.sql` | Status enum update | | 015 | `015_add_candidate_profile_fields.sql` | Candidate profile | | 016 | `016_create_notifications_table.sql` | Notifications | | 017 | `017_create_tickets_table.sql` | Support tickets | | 018 | `018_add_currency_to_jobs.sql` | Currency support | | 019 | `019_create_job_payments_table.sql` | Payment tracking | | 020 | `020_unify_schema.sql` | Schema unification | | 999 | `999_fix_gohorse_schema.sql` | Schema fixes | --- ## ⚠️ ID Strategy Notes The database uses a **hybrid ID strategy**: | Strategy | Tables | Rationale | |----------|--------|-----------| | **SERIAL (INT)** | users, companies, jobs, applications, regions, cities | Legacy tables, simpler, faster, auto-increment | | **UUID v7** | notifications, tickets, job_payments | Time-ordered, distributed-friendly, sortable | ### UUID v7 (RFC 9562) Starting from migration `021_create_uuid_v7_function.sql`, the database uses **UUID v7** instead of UUID v4: ```sql -- UUID v7 is generated by uuid_generate_v7() function SELECT uuid_generate_v7(); -- Returns: 019438a1-2b3c-7abc-8123-4567890abcdef -- ^^^^^^^^ time component (sortable) ``` **Benefits of UUID v7:** - ⏱️ Time-ordered (sortable by creation time) - 🌐 Distributed-friendly (no coordination needed) - πŸ“Š Better index performance than UUID v4 - πŸ”’ Contains embedded timestamp --- ## πŸ”’ Security Notes 1. **Password Hashing:** BCrypt with optional `PASSWORD_PEPPER` environment variable 2. **Soft Deletes:** Not implemented (uses `CASCADE` for referential integrity) 3. **Row-Level Security:** Not implemented (uses application-level checks) 4. **Audit Trail:** `login_audits` and `activity_logs` for compliance --- ## πŸ“š Related Documentation - [Backend README](../backend/README.md) - API documentation - [Seeder README](../seeder-api/README.md) - Database seeding - [Backoffice README](../backoffice/README.md) - Admin panel