Skip to content

Veritabanı Şeması (Database Schema Definition)

Bu doküman, Humindx platformunun PostgreSQL veritabanı şemasını, tablo ilişkilerini, indeksleri ve Row-Level Security (RLS) politikalarını tanımlar. Tüm doküman boyunca referans edilen tablolar (context_rooms, trait_scores, audit_events, consent_grants vb.) burada tek bir kaynakta birleştirilmiştir.

Kapsam Uyarısı: Bu doküman, veritabanı yapısını ve migration stratejisini kapsar. RLS politikalarının mimari gerekçeleri için context-rooms-design.md, audit loglarının olay yapısı için ../security/audit-trail.md dosyasına bakınız.


1. ER Diyagramı (Entity-Relationship)


2. Tablo Detayları ve İndeksler

2.1. users

Kimlik veritabanı (Identity DB). Psikometrik motorun erişemediği alan.

sql
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email TEXT NOT NULL UNIQUE,  -- AES-256 ile şifreli
    password_hash TEXT NOT NULL,
    locale VARCHAR(10) DEFAULT 'tr-TR',
    genome_id UUID NOT NULL UNIQUE,  -- Anonim referans
    subscription VARCHAR(20) DEFAULT 'FREE',
    created_at TIMESTAMPTZ DEFAULT NOW(),
    deleted_at TIMESTAMPTZ  -- NULL = aktif, SET = ghost node
);

CREATE INDEX idx_users_genome ON users(genome_id);
CREATE INDEX idx_users_email ON users(email);

2.2. context_rooms

Kullanıcı başına 4 oda. Oluşturma: kayıt anında otomatik.

sql
CREATE TABLE context_rooms (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    room_type VARCHAR(20) NOT NULL CHECK (room_type IN ('PROFESSIONAL', 'SOCIAL', 'CLINICAL', 'DISCOVERY')),
    created_at TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(user_id, room_type)
);

2.3. trait_scores

Materialized State — Odalar bazlı güncel skorlar.

sql
CREATE TABLE trait_scores (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    room_id UUID NOT NULL REFERENCES context_rooms(id) ON DELETE CASCADE,
    trait_name VARCHAR(50) NOT NULL,  -- 'bigfive_openness', 'riasec_social' vb.
    current_score FLOAT NOT NULL CHECK (current_score >= 0 AND current_score <= 100),
    confidence_score FLOAT DEFAULT 0.0 CHECK (confidence_score >= 0 AND confidence_score <= 1),
    interaction_count INT DEFAULT 0,
    metadata JSONB DEFAULT '{}',
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    UNIQUE(room_id, trait_name)
);

-- RLS aktifleştirme
ALTER TABLE trait_scores ENABLE ROW LEVEL SECURITY;

-- B2B Gateway rolü sadece PROFESSIONAL odayı görebilir
CREATE POLICY b2b_isolation ON trait_scores
FOR SELECT TO b2b_gateway_role
USING (
    room_id IN (
        SELECT id FROM context_rooms
        WHERE room_type = 'PROFESSIONAL'
    )
);

CREATE INDEX idx_trait_room ON trait_scores(room_id);
CREATE INDEX idx_trait_name ON trait_scores(trait_name);

2.4. audit_events

Append-only, immutable log. Hash chaining ile korunan.

sql
CREATE TABLE audit_events (
    event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    genome_id UUID NOT NULL,  -- FK yok: ghost node sonrası bile yaşar
    timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    actor_type VARCHAR(10) NOT NULL CHECK (actor_type IN ('SYSTEM', 'LLM', 'USER')),
    event_type VARCHAR(30) NOT NULL,
    context_room VARCHAR(20),
    payload JSONB NOT NULL,
    event_hash TEXT NOT NULL,
    prev_hash TEXT NOT NULL,
    
    -- Bu tabloya UPDATE ve DELETE yasak
    CHECK (TRUE)  -- Uygulama seviyesinde enforce
);

-- Zaman bazlı sorgular için
CREATE INDEX idx_audit_genome_time ON audit_events(genome_id, timestamp DESC);
CREATE INDEX idx_audit_event_type ON audit_events(event_type);

-- Append-only garantisi: DBA bile silemez
REVOKE DELETE, UPDATE ON audit_events FROM PUBLIC;
REVOKE DELETE, UPDATE ON audit_events FROM app_role;

Akıllı Gizlilik Sözleşmesi izin tablosu.

sql
CREATE TABLE consent_grants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES tenants(id),
    target_workspace VARCHAR(20) NOT NULL CHECK (target_workspace IN ('hiring', 'talent')),
    level INT NOT NULL CHECK (level IN (1, 2, 3)),
    is_active BOOLEAN DEFAULT TRUE,
    granted_at TIMESTAMPTZ DEFAULT NOW(),
    revoked_at TIMESTAMPTZ
);

CREATE INDEX idx_consent_user_tenant ON consent_grants(user_id, tenant_id);
CREATE INDEX idx_consent_active ON consent_grants(is_active) WHERE is_active = TRUE;

2.6. vector_embeddings

Gürültü enjekte edilmiş vektörler. RAG pipeline'ı besler.

sql
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE vector_embeddings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    genome_id UUID NOT NULL,
    room_id UUID NOT NULL REFERENCES context_rooms(id) ON DELETE CASCADE,
    embedding vector(1536),  -- OpenAI text-embedding-3-small boyutu
    source_label VARCHAR(10) CHECK (source_label IN ('PRO', 'SOCIAL', 'NEUTRAL')),
    epsilon_used FLOAT NOT NULL,  -- DP gürültü bütçesi kaydı
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- RAG sorguları için HNSW indeksi
CREATE INDEX idx_vector_hnsw ON vector_embeddings
    USING hnsw (embedding vector_cosine_ops)
    WITH (m = 16, ef_construction = 64);

-- Oda filtresi (RAG'da SOCIAL hariç tutma) için
CREATE INDEX idx_vector_room_label ON vector_embeddings(room_id, source_label);

2.7. B2B Tabloları (tenants, workspaces, tenant_members, culture_templates)

sql
CREATE TABLE tenants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    company_name TEXT NOT NULL,
    culture_dna JSONB,
    active_culture_version VARCHAR(10),
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE workspaces (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    type VARCHAR(20) NOT NULL CHECK (type IN ('hiring', 'talent'))
);

CREATE TABLE tenant_members (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
    user_id UUID NOT NULL,  -- B2B panel kullanıcısı (İK, yönetici)
    role VARCHAR(20) NOT NULL CHECK (role IN ('HR_ADMIN', 'RECRUITER', 'TEAM_LEADER')),
    team_id UUID  -- Sadece TEAM_LEADER için
);

CREATE TABLE culture_templates (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
    version VARCHAR(10) NOT NULL,
    ideal_profile JSONB NOT NULL,
    riasec_target VARCHAR(10),
    effective_date TIMESTAMPTZ DEFAULT NOW(),
    is_active BOOLEAN DEFAULT TRUE
);

-- Workspace bazlı RLS
ALTER TABLE tenant_members ENABLE ROW LEVEL SECURITY;

2.8. chat_sessions ve passport_shares

sql
CREATE TABLE chat_sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    genome_id UUID NOT NULL,
    agent_type VARCHAR(20) NOT NULL,
    scenario_label VARCHAR(10),
    label_overridden BOOLEAN DEFAULT FALSE,
    status VARCHAR(20) DEFAULT 'ACTIVE',
    started_at TIMESTAMPTZ DEFAULT NOW(),
    ended_at TIMESTAMPTZ
);

CREATE TABLE passport_shares (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    signed_token TEXT NOT NULL,
    granted_level INT NOT NULL CHECK (granted_level IN (1, 2, 3)),
    expires_at TIMESTAMPTZ NOT NULL,
    is_revoked BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

3. Migration Stratejisi

AşamaKapsamAraç
GeliştirmeLokal Docker PostgreSQL, seed data ilePrisma Migrate veya golang-migrate
StagingManaged PostgreSQL (RDS/Cloud SQL), RLS politikalarının testiCI/CD pipeline'da otomatik migration
ProductionBlue-Green deployment, zero-downtime migrationFlyway veya Prisma Migrate + manual review

Kritik Kurallar:

  • audit_events tablosuna asla destructive migration (DROP COLUMN, ALTER TYPE) uygulanmaz. Yeni alanlar sadece eklenir (additive-only).
  • RLS politikaları her migration'da test edilir: "B2B rolü SOCIAL odayı görebiliyor mu?" assertion'ı CI'da çalışır.

4. Seed Data (Geliştirme Ortamı)

environment-setup.md'deki Docker kurulumu sonrası, test verileriyle hızlı başlangıç için:

sql
-- Test kullanıcısı
INSERT INTO users (id, email, password_hash, genome_id, subscription)
VALUES ('uuid-test-1', 'test@humindx.dev', '$2b$...', 'genome-test-1', 'PREMIUM');

-- 4 Bağlam Odası
INSERT INTO context_rooms (user_id, room_type) VALUES
    ('uuid-test-1', 'PROFESSIONAL'),
    ('uuid-test-1', 'SOCIAL'),
    ('uuid-test-1', 'CLINICAL'),
    ('uuid-test-1', 'DISCOVERY');

-- Başlangıç çapası skorları (S0)
INSERT INTO trait_scores (room_id, trait_name, current_score, confidence_score)
SELECT cr.id, trait.name, trait.score, 0.14
FROM context_rooms cr
CROSS JOIN (VALUES
    ('bigfive_openness', 65), ('bigfive_conscientiousness', 55),
    ('bigfive_extraversion', 70), ('bigfive_agreeableness', 80),
    ('bigfive_neuroticism', 35)
) AS trait(name, score)
WHERE cr.user_id = 'uuid-test-1' AND cr.room_type = 'DISCOVERY';

-- Test tenant
INSERT INTO tenants (id, company_name, active_culture_version)
VALUES ('tenant-test-1', 'Acme Corp Test', 'v1');

Son Güncelleme: 2026-04-15 — Tam ER diyagramı, SQL DDL (RLS dahil), HNSW vektör indeksi, migration stratejisi ve seed data tanımlandı.

Simetri tarafından inşa ediliyor.