Skip to main content

Database Overview

PaparcApp uses PostgreSQL as its relational database with a normalized schema consisting of 13 tables. The database is designed to support:
  • Customer and vehicle management
  • Reservation lifecycle (pending → in progress → finalized)
  • Dynamic pricing with vehicle coefficients and rate tiers
  • Additional services and subscription plans
  • Photo evidence and notification tracking

Entity Relationship Diagram

┌──────────────┐         ┌──────────────────┐         ┌──────────────┐
│  CUSTOMER    │◄───────►│ CUSTOMER_VEHICLE │◄───────►│   VEHICLE    │
│              │  N:M    │    (junction)    │  N:M    │              │
│ id_customer  │         │                  │         │ id_vehicle   │
│ full_name    │         └──────────────────┘         │ license_plate│
│ email        │                                      │ brand/model  │
│ phone        │         ┌──────────────────┐         │ color/type   │
│ type (role)  │◄────┐   │    CONTRACT      │     ┌──►│              │
└──────────────┘     │   │                  │     │   └──────┬───────┘
                     │   │ id_contract      │     │          │
                     └───┤ id_customer (FK) │     │          │
                         │ id_vehicle (FK)  │─────┘          │
                         │ id_plan (FK)     │                │
                         │ start/end_date   │                │
                         └────────┬─────────┘                │
                                  │                          │
                         ┌────────▼─────────┐                │
                         │ CONTRACT_PLAN    │                │
                         │                  │                │
                         │ id_plan          │                │
                         │ name             │                │
                         │ duration_months  │                │
                         │ price            │                │
                         └──────────────────┘                │

┌──────────────┐         ┌──────────────────┐               │
│ MAIN_SERVICE │◄────┐   │   RESERVATION    │◄──────────────┘
│              │     │   │                  │
│id_main_service    │   │ id_reservation   │
│ name (ECO,   │     └───┤ id_customer (FK) │
│ TRANSFER,    │         │ id_vehicle (FK)  │
│ MEET)        │         │ id_main_service  │
└──────┬───────┘         │ entry/exit_date  │
       │                 │ status           │
       │                 │ total_price      │
       │                 │ cod_parking_spot │
       │                 └────┬─────┬───────┘
       │                      │     │
       │                      │     └──────────────┐
       │                      │                    │
┌──────▼───────┐         ┌───▼───────────┐   ┌────▼──────────┐
│ SERVICE_RATE │         │ NOTIFICATION  │   │ PHOTO_EVIDENCE│
│              │         │               │   │               │
│ id_rate      │         │id_notification│   │ id_photo      │
│ min_days     │         │id_reservation │   │id_reservation │
│ max_days     │         │ subject       │   │ file_path     │
│ daily_price  │         │ message       │   │ description   │
│id_main_service        │ type          │   └───────────────┘
└──────────────┘         └───────────────┘

┌─────────────────┐     ┌────────────────────────────┐     ┌─────────────────────┐
│ADDITIONAL_SERVICE◄────┤ RESERVATION_ADDITIONAL     │────►│    RESERVATION      │
│                 │ N:M │ _SERVICE (junction)        │ N:M │                     │
│id_additional_   │     │                            │     └─────────────────────┘
│  service        │     │ id_reservation (FK)        │
│ name            │     │ id_additional_service (FK) │
│ category        │     └────────────────────────────┘
│ price           │
└─────────────────┘

┌──────────────────────┐
│ VEHICLE_COEFFICIENT  │  (Master table for pricing)
│                      │
│ vehicle_type (PK)    │
│ multiplier           │
└──────────────────────┘

Table Definitions

1. CUSTOMER

Stores customer information with three user types: ADMIN, REGISTRADO (registered), and NO-REGISTRADO (guest).
CREATE TABLE customer (
    id_customer       SERIAL PRIMARY KEY,
    full_name         VARCHAR(100) NOT NULL,
    email             VARCHAR(150) UNIQUE,
    phone             VARCHAR(20) UNIQUE,
    password_hash     VARCHAR(255),
    registration_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    type              VARCHAR(30), 
    is_active         BOOLEAN NOT NULL DEFAULT TRUE
);
Key Fields:
  • id_customer: Auto-incrementing primary key
  • email / phone: Unique identifiers for user lookup
  • password_hash: bcrypt-hashed password (NULL for guest users)
  • type: User role (ADMIN, REGISTRADO, NO-REGISTRADO)
  • is_active: Soft delete flag
Constraints:
ALTER TABLE customer
    ADD CONSTRAINT chk_customer_type
    CHECK (type IN ('ADMIN', 'REGISTRADO', 'NO-REGISTRADO'));

ALTER TABLE customer
    ADD CONSTRAINT chk_customer_email_format
    CHECK (email IS NULL OR email LIKE '%_@__%.__%');

ALTER TABLE customer
    ADD CONSTRAINT chk_customer_phone_format
    CHECK (LENGTH(phone) >= 7 OR phone IS NULL);

2. VEHICLE

Stores vehicle information independent of ownership (many-to-many relationship with customers).
CREATE TABLE vehicle (
    id_vehicle         SERIAL PRIMARY KEY,
    license_plate      VARCHAR(15) NOT NULL UNIQUE,
    brand              VARCHAR(50) NOT NULL,
    model              VARCHAR(50) NOT NULL,
    color              VARCHAR(30) NOT NULL, 
    type               VARCHAR(30) NOT NULL, 
    registration_date  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Key Fields:
  • license_plate: Unique vehicle identifier
  • type: Vehicle category (TURISMO, MOTOCICLETA, FURGONETA, CARAVANA, ESPECIAL)
  • type is a foreign key to vehicle_coefficient.vehicle_type for pricing
Foreign Key:
ALTER TABLE vehicle
    ADD CONSTRAINT fk_vehicle_type
    FOREIGN KEY (type)
    REFERENCES vehicle_coefficient(vehicle_type)
    ON DELETE RESTRICT
    ON UPDATE CASCADE;

3. CUSTOMER_VEHICLE

Junction table for the many-to-many relationship between customers and vehicles.
CREATE TABLE customer_vehicle (
    id_customer     INT NOT NULL,
    id_vehicle      INT NOT NULL,
    linked_at       TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id_customer, id_vehicle)
);
Purpose: Customers can own multiple vehicles, and vehicles can be shared (e.g., family members) Foreign Keys:
ALTER TABLE customer_vehicle
    ADD CONSTRAINT fk_cv_customer
    FOREIGN KEY (id_customer) REFERENCES customer(id_customer)
    ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE customer_vehicle
    ADD CONSTRAINT fk_cv_vehicle
    FOREIGN KEY (id_vehicle) REFERENCES vehicle(id_vehicle)
    ON DELETE CASCADE ON UPDATE CASCADE;
Index:
CREATE INDEX idx_search_owners_by_vehicle_id 
    ON customer_vehicle(id_vehicle);

4. MAIN_SERVICE

Defines the three core parking services offered by PaparcApp.
CREATE TABLE main_service (
    id_main_service   SERIAL PRIMARY KEY,
    name              VARCHAR(100) NOT NULL UNIQUE,
    tagline           VARCHAR(150) NOT NULL, 
    full_description  TEXT NOT NULL,                 
    is_active         BOOLEAN NOT NULL DEFAULT TRUE
);
Services:
  1. ECO - Walk to terminal (cheapest)
  2. TRANSFER - Minibus to terminal
  3. MEET - Premium valet service
Sample Data:
INSERT INTO main_service (name, tagline, full_description) VALUES 
('ECO', 'Arrive, hand over your keys and walk straight to your flight.', 
 'Vehicle reception by our staff|Professional parking guaranteed|...'),
('TRANSFER', 'Hand over your car and we take you to the terminal by minibus.', 
 'Parking performed by staff|VIP transfer in courtesy minibus|...'),
('MEET', 'Premium Service: Pick-up and drop-off at the terminal.', 
 'Professional chauffeur awaits you at the terminal|...');

5. SERVICE_RATE

Pricing tiers based on service type and stay duration (days).
CREATE TABLE service_rate (
    id_rate         SERIAL PRIMARY KEY,
    min_days        INT NOT NULL,
    max_days        INT NOT NULL,
    daily_price     NUMERIC(8,2) NOT NULL,
    id_main_service INT NOT NULL -- FK
);
Example Rates:
INSERT INTO service_rate (id_main_service, min_days, max_days, daily_price) VALUES
-- ECO Service
(1, 1, 3, 12.00),    -- 1-3 days: €12/day
(1, 4, 10, 8.00),    -- 4-10 days: €8/day
(1, 11, 15, 6.00),   -- 11-15 days: €6/day
(1, 16, 9999, 5.00), -- 16+ days: €5/day

-- TRANSFER Service
(2, 1, 3, 15.00),
(2, 4, 10, 11.00),
(2, 11, 15, 9.00),
(2, 16, 9999, 8.00),

-- MEET Service (Premium)
(3, 1, 3, 18.00),
(3, 4, 10, 14.00),
(3, 11, 15, 12.00),
(3, 16, 9999, 11.00);
Constraints:
ALTER TABLE service_rate
    ADD CONSTRAINT chk_rate_days_range
    CHECK (min_days > 0 AND max_days >= min_days);

ALTER TABLE service_rate
    ADD CONSTRAINT chk_rate_positive_price
    CHECK (daily_price >= 0);

-- Prevent overlapping rate tiers
ALTER TABLE service_rate
    ADD CONSTRAINT unique_rate_days
    UNIQUE (id_main_service, min_days);
Foreign Key:
ALTER TABLE service_rate
    ADD CONSTRAINT fk_service_rate_main_service
    FOREIGN KEY (id_main_service) REFERENCES main_service(id_main_service)
    ON DELETE CASCADE ON UPDATE CASCADE;

6. VEHICLE_COEFFICIENT

Master table for vehicle pricing multipliers.
CREATE TABLE vehicle_coefficient (
    vehicle_type    VARCHAR(30) PRIMARY KEY,
    multiplier      NUMERIC(4,2) NOT NULL DEFAULT 1.00
);
Pricing Multipliers:
INSERT INTO vehicle_coefficient (vehicle_type, multiplier) VALUES 
('TURISMO', 1.00),      -- Standard car (baseline)
('MOTOCICLETA', 0.50),  -- Motorcycle (50% discount)
('FURGONETA', 1.25),    -- Van (25% premium)
('CARAVANA', 2.00),     -- Caravan (100% premium)
('ESPECIAL', 1.50);     -- Special vehicles (50% premium)
Constraint:
ALTER TABLE vehicle_coefficient
    ADD CONSTRAINT chk_vehicle_coefficient_positive
    CHECK (multiplier > 0);

7. ADDITIONAL_SERVICE

Optional services customers can add to reservations (car wash, refueling, etc.).
CREATE TABLE additional_service (
    id_additional_service SERIAL PRIMARY KEY,
    name                  VARCHAR(100) NOT NULL UNIQUE,
    category              VARCHAR(50) NOT NULL, 
    tagline               VARCHAR(150),         
    price                 NUMERIC(8,2) NOT NULL,
    features              TEXT,                 
    is_active             BOOLEAN NOT NULL DEFAULT TRUE
);
Categories:
  • CLEANING: Washes, interior cleaning, detailing
  • MANAGEMENT: Refueling, MOT service
  • MAINTENANCE: Fluid checks, tire pressure
  • ENERGY: EV charging
Sample Services:
INSERT INTO additional_service (name, category, tagline, price, features) VALUES 
('Basic Wash', 'CLEANING', 'Shine on the outside.', 15.00, 
 'Hand exterior wash|Microfiber drying|Wheel and tire cleaning'),
('Interior Cleaning', 'CLEANING', 'Hygiene and freshness inside.', 25.00, 
 'Deep vacuuming of mats|Technical dashboard cleaning|Air duct disinfection'),
('Full Wash', 'CLEANING', 'Your car, as good as new.', 50.00, 
 'Upholstery cleaning|Premium exterior wash|Odor removal with ozone'),
('Refueling', 'MANAGEMENT', 'No stops when leaving the parking.', 15.00, 
 'Tank filled before delivery|Time saving|Fuel at market price'),
('EV Charging', 'ENERGY', '100% battery upon landing.', 25.00, 
 'Full electric charge|Compatible with all models');
Constraint:
ALTER TABLE additional_service
    ADD CONSTRAINT chk_additional_service_price
    CHECK (price >= 0);

8. RESERVATION

Core table tracking parking reservations with lifecycle management.
CREATE TABLE reservation (
    id_reservation    SERIAL PRIMARY KEY,
    reservation_date  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    entry_date        TIMESTAMP NOT NULL,
    exit_date         TIMESTAMP,
    status            VARCHAR(30) NOT NULL DEFAULT 'PENDIENTE',
    total_price       NUMERIC(10,2) NOT NULL,
    is_paid           BOOLEAN NOT NULL DEFAULT FALSE,
    payment_method    VARCHAR(30),
    notes             TEXT,
    id_customer       INT NOT NULL, -- FK
    id_vehicle        INT NOT NULL, -- FK
    id_main_service   INT NOT NULL, -- FK
    cod_parking_spot  VARCHAR(20)
);
Status Flow:
PENDIENTE → EN CURSO → FINALIZADA

              CANCELADA
Key Fields:
  • reservation_date: When reservation was created
  • entry_date: When vehicle enters parking
  • exit_date: When vehicle leaves (NULL if not yet determined)
  • status: Current reservation state
  • cod_parking_spot: Assigned parking spot (NULL until vehicle arrives)
  • is_paid: Payment status
  • payment_method: TARJETA (card) or EFECTIVO (cash)
Foreign Keys:
ALTER TABLE reservation
    ADD CONSTRAINT fk_reservation_customer
    FOREIGN KEY (id_customer) REFERENCES customer(id_customer)
    ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE reservation
    ADD CONSTRAINT fk_reservation_vehicle
    FOREIGN KEY (id_vehicle) REFERENCES vehicle(id_vehicle)
    ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE reservation
    ADD CONSTRAINT fk_reservation_main_service
    FOREIGN KEY (id_main_service) REFERENCES main_service(id_main_service)
    ON DELETE RESTRICT ON UPDATE CASCADE;
Complex Constraints:
-- Status must be valid
ALTER TABLE reservation
    ADD CONSTRAINT chk_reservation_status
    CHECK (status IN ('PENDIENTE', 'EN CURSO', 'FINALIZADA', 'CANCELADA'));

-- Payment method validation
ALTER TABLE reservation
    ADD CONSTRAINT chk_reservation_payment_method
    CHECK (payment_method IN ('TARJETA', 'EFECTIVO') OR payment_method IS NULL);

-- No negative prices
ALTER TABLE reservation
    ADD CONSTRAINT chk_reservation_total_price
    CHECK (total_price >= 0);

-- Exit must be after entry
ALTER TABLE reservation
    ADD CONSTRAINT chk_reservation_exit_date
    CHECK (exit_date IS NULL OR exit_date >= entry_date);

-- Parking spot required for active/finished reservations
ALTER TABLE reservation
    ADD CONSTRAINT chk_spot_assigned
    CHECK (
        (status IN ('EN CURSO', 'FINALIZADA') AND cod_parking_spot IS NOT NULL)
        OR
        (status IN ('PENDIENTE', 'CANCELADA'))
    );

-- Must be paid to finalize
ALTER TABLE reservation
    ADD CONSTRAINT chk_reservation_must_be_paid
    CHECK (
        (status = 'FINALIZADA' AND is_paid = TRUE)
        OR
        (status <> 'FINALIZADA')
    );
Indexes (for performance):
CREATE INDEX idx_search_reservation_by_vehicle_id 
    ON reservation(id_vehicle);

CREATE INDEX idx_search_reservation_by_customer_id 
    ON reservation(id_customer);

CREATE INDEX idx_search_reservation_by_entry_date 
    ON reservation(entry_date);

CREATE INDEX idx_search_reservation_by_exit_date 
    ON reservation(exit_date);

-- Composite indexes for common queries
CREATE INDEX idx_search_reservation_by_entry_date_status 
    ON reservation(entry_date, status);

CREATE INDEX idx_search_reservation_by_exit_date_status 
    ON reservation(exit_date, status);

9. RESERVATION_ADDITIONAL_SERVICE

Junction table linking reservations to additional services (many-to-many).
CREATE TABLE reservation_additional_service (
    id_reservation        INT NOT NULL,
    id_additional_service INT NOT NULL,
    PRIMARY KEY (id_reservation, id_additional_service)
);
Foreign Keys:
ALTER TABLE reservation_additional_service
    ADD CONSTRAINT fk_ras_reservation
    FOREIGN KEY (id_reservation) REFERENCES reservation(id_reservation)
    ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE reservation_additional_service
    ADD CONSTRAINT fk_ras_additional_service
    FOREIGN KEY (id_additional_service) REFERENCES additional_service(id_additional_service)
    ON DELETE RESTRICT ON UPDATE CASCADE;
Usage Example:
-- Reservation #7 includes MOT Service and Basic Wash
INSERT INTO reservation_additional_service (id_reservation, id_additional_service) 
VALUES (7, 6), (7, 1);

10. PHOTO_EVIDENCE

Stores file paths for vehicle condition photos (5 photos minimum per reservation).
CREATE TABLE photo_evidence (
    id_photo          SERIAL PRIMARY KEY,
    file_path         VARCHAR(255) NOT NULL,
    description       VARCHAR(200),
    taken_at          TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    id_reservation    INT NOT NULL --FK
);
Standard Photos:
  1. Front view
  2. Back view
  3. Left side
  4. Right side
  5. Dashboard
Foreign Key:
ALTER TABLE photo_evidence
    ADD CONSTRAINT fk_photo_reservation
    FOREIGN KEY (id_reservation) REFERENCES reservation(id_reservation)
    ON DELETE CASCADE ON UPDATE CASCADE;
Index:
CREATE INDEX idx_search_photo_by_reservation_id 
    ON photo_evidence(id_reservation);

11. NOTIFICATION

Audit log of communications sent to customers.
CREATE TABLE notification (
    id_notification   SERIAL PRIMARY KEY,
    subject           VARCHAR(150) NOT NULL,
    message           TEXT NOT NULL,
    type              VARCHAR(50) NOT NULL,
    sent_at           TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    id_reservation    INT NOT NULL --FK
);
Notification Types:
ALTER TABLE notification
    ADD CONSTRAINT chk_notification_type
    CHECK (type IN (
        'TICKET_RESERVA',           -- Reservation confirmation
        'ENTRADA_CONFIRMADA',       -- Vehicle received
        'RECIBO_PAGO',              -- Payment receipt
        'ACTUALIZACION_RESERVA'     -- Reservation updated
    ));
Foreign Key:
ALTER TABLE notification
    ADD CONSTRAINT fk_notification_reservation
    FOREIGN KEY (id_reservation) REFERENCES reservation(id_reservation)
    ON DELETE CASCADE ON UPDATE CASCADE;
Index:
CREATE INDEX idx_search_notification_by_reservation_id 
    ON notification(id_reservation);

12. CONTRACT_PLAN

Subscription plans for frequent customers.
CREATE TABLE contract_plan (
    id_plan          SERIAL PRIMARY KEY,
    name             VARCHAR(50) NOT NULL UNIQUE,
    duration_months  INT NOT NULL,
    price            NUMERIC(8,2) NOT NULL,
    tagline          VARCHAR(150), 
    features         TEXT,         
    is_active        BOOLEAN NOT NULL DEFAULT TRUE
);
Available Plans:
INSERT INTO contract_plan (name, duration_months, price, tagline, features) VALUES
('Quarterly', 3, 325.00, 'The perfect solution for your seasonal trips.', 
 'ECO and TRANSFER services included|100% guaranteed priority spot|...'),
('Semiannual', 6, 590.00, 'Save and travel with total freedom for half a year.', 
 'ECO and TRANSFER services included|1 complimentary full wash|...'),
('Annual', 12, 999.00, 'Maximum peace of mind and savings for expert travelers.', 
 'Fixed VIP spot guaranteed|2 full washes per year|10% discount on extras|...');
Constraint:
ALTER TABLE contract_plan
    ADD CONSTRAINT chk_contract_plan_values
    CHECK (duration_months > 0 AND price > 0);

13. CONTRACT

Active subscriptions linking customers and vehicles to plans.
CREATE TABLE contract (
    id_contract     SERIAL PRIMARY KEY,
    start_date      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    end_date        TIMESTAMP NOT NULL,
    is_active       BOOLEAN NOT NULL DEFAULT TRUE,
    id_customer     INT NOT NULL, -- FK
    id_vehicle      INT NOT NULL, -- FK
    id_plan         INT NOT NULL  -- FK
);
Foreign Keys:
ALTER TABLE contract
    ADD CONSTRAINT fk_contract_customer
    FOREIGN KEY (id_customer) REFERENCES customer(id_customer)
    ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE contract
    ADD CONSTRAINT fk_contract_vehicle
    FOREIGN KEY (id_vehicle) REFERENCES vehicle(id_vehicle)
    ON DELETE RESTRICT ON UPDATE CASCADE;

ALTER TABLE contract
    ADD CONSTRAINT fk_contract_plan
    FOREIGN KEY (id_plan) REFERENCES contract_plan(id_plan)
    ON DELETE RESTRICT ON UPDATE CASCADE;
Constraint:
ALTER TABLE contract
    ADD CONSTRAINT chk_contract_dates
    CHECK (end_date > start_date);

Database Relationships Summary

One-to-Many Relationships

Parent TableChild TableRelationship
customerreservationOne customer → many reservations
vehiclereservationOne vehicle → many reservations
main_servicereservationOne service → many reservations
main_serviceservice_rateOne service → many rate tiers
reservationphoto_evidenceOne reservation → many photos
reservationnotificationOne reservation → many notifications
contract_plancontractOne plan → many contracts

Many-to-Many Relationships

Entity 1Junction TableEntity 2Meaning
customercustomer_vehiclevehicleCustomers can own/share multiple vehicles
reservationreservation_additional_serviceadditional_serviceReservations can have multiple add-ons

Master-Detail Relationships

Master TableDetail TablePurpose
vehicle_coefficientvehicleVehicle types must exist in master table
main_serviceservice_rateRate tiers defined per service

Indexes Overview

All indexes created for query performance optimization:
-- Reservation lookups (most frequent operations)
CREATE INDEX idx_search_reservation_by_vehicle_id ON reservation(id_vehicle);
CREATE INDEX idx_search_reservation_by_customer_id ON reservation(id_customer);
CREATE INDEX idx_search_reservation_by_entry_date ON reservation(entry_date);
CREATE INDEX idx_search_reservation_by_exit_date ON reservation(exit_date);

-- Composite indexes for dashboard queries
CREATE INDEX idx_search_reservation_by_entry_date_status 
    ON reservation(entry_date, status);
CREATE INDEX idx_search_reservation_by_exit_date_status 
    ON reservation(exit_date, status);

-- Vehicle ownership lookups
CREATE INDEX idx_search_owners_by_vehicle_id ON customer_vehicle(id_vehicle);

-- Related records
CREATE INDEX idx_search_photo_by_reservation_id ON photo_evidence(id_reservation);
CREATE INDEX idx_search_notification_by_reservation_id ON notification(id_reservation);
Note: Primary keys and UNIQUE constraints automatically create indexes, so no manual index creation is needed for those columns.

Constraint Summary

Data Integrity Constraints

  • Foreign Keys: 15 foreign key relationships ensuring referential integrity
  • Check Constraints: 16 validation rules for business logic
  • Unique Constraints: 8 uniqueness requirements (email, phone, license_plate, etc.)
  • Not Null Constraints: Critical fields cannot be NULL

Business Logic Constraints

  1. Reservation Lifecycle: Status transitions must follow PENDIENTE → EN CURSO → FINALIZADA
  2. Payment Validation: FINALIZADA reservations must be marked as paid
  3. Parking Spot Assignment: EN CURSO and FINALIZADA require assigned spots
  4. Date Validation: exit_date must be after entry_date
  5. Pricing Validation: All prices must be non-negative
  6. Rate Tiers: No overlapping day ranges for same service

Database Initialization

The database is initialized using 4 SQL scripts:
  1. 01_tables.sql: Creates all 13 tables
  2. 02_constraints.sql: Adds foreign keys and check constraints
  3. 03_indexes.sql: Creates performance indexes
  4. 04_initial_data.sql: Loads seed data (services, rates, coefficients, sample users/reservations)
Execution Order:
psql -U user -d database < 01_tables.sql
psql -U user -d database < 02_constraints.sql
psql -U user -d database < 03_indexes.sql
psql -U user -d database < 04_initial_data.sql

Connection Configuration

PaparcApp connects to PostgreSQL using a connection pool:
const { Pool } = require('pg');

const pool = new Pool({
    connectionString: process.env.DATABASE_URL,
    ssl: {
        rejectUnauthorized: false // Required for cloud databases
    }
});
Environment Variable:
DATABASE_URL=postgresql://user:password@host:5432/database?sslmode=require
The pool automatically manages connections, providing:
  • Connection reuse
  • Automatic reconnection
  • Concurrent query handling
  • Connection limits and timeouts

Summary

The PaparcApp database schema is:
  • Normalized to 3rd Normal Form (3NF)
  • Performance-optimized with strategic indexes
  • Integrity-protected with comprehensive constraints
  • Cloud-ready with SSL support and connection pooling
  • Well-documented with inline SQL comments and constraint naming
The schema supports the full reservation lifecycle from booking through payment, with robust pricing calculations, customer management, and audit trails.

Build docs developers (and LLMs) love