Skip to content

3. Data ArchitectureΒΆ

3.1 Layered Data ArchitectureΒΆ

graph TD
  subgraph PRESENTATION["πŸ“Š Presentation Layer"]
    P1["Grafana\nOperational"] ~~~ P2["Metabase\nProperty Analytics"] ~~~ P3["GeoServer\nMap Tiles"]
  end
  subgraph ANALYTICS["🧠 Analytics Layer"]
    A1["ClickHouse\nProperty Analytics"] ~~~ A2["MLflow\nValuation Models"]
  end
  subgraph PROCESSING["βš™οΈ Data Processing"]
    D1["Kafka\nEvent Streaming"] ~~~ D2["Airflow\nETL / OCR Pipelines"]
  end
  subgraph STORAGE["πŸ’Ύ Storage Layer"]
    S1["PostgreSQL\n+ PostGIS"] ~~~ S2["Redis\nCache"] ~~~ S3["MinIO\nDocuments & Maps"] ~~~ S4["MongoDB\nForms & Logs"]
  end
  subgraph SPATIAL["πŸ—ΊοΈ Spatial Data"]
    G1["PostGIS\nParcel Geometries"] ~~~ G2["GeoServer\nWMS/WFS"] ~~~ G3["OpenLayers\nMap Viewer"]
  end
  subgraph INTEGRATION["πŸ”— Integration"]
    I1["Kong API\nGateway"] ~~~ I2["KRG-Road\nSecurity Server"] ~~~ I3["Legacy\nAdapters"]
  end

  PRESENTATION --> ANALYTICS --> PROCESSING --> STORAGE
  STORAGE --> SPATIAL
  STORAGE --> INTEGRATION

3.2 Database StrategyΒΆ

Database Type Use Case Version
PostgreSQL + PostGIS Relational + Spatial Properties, parcels, buildings, transactions, permits, GIS geometries 16+ with Citus + PostGIS 3.4
MongoDB Document Dynamic form schemas, inspection checklists, audit event details 7.0+
Redis In-Memory Cache Session, rate limiting, cached valuations, work queues 7.2+ (Redis Stack)
ClickHouse Columnar (OLAP) Property market analytics, trend reporting, KPIs 24.x
MinIO Object Storage Scanned deeds, building plans, photos, GIS raster data Latest
Apache Kafka Event Streaming Property events (transfers, permits), CDC, inter-service comms 3.7+ (KRaft)
Vault (HashiCorp) Secrets Encryption keys, DB credentials, API keys 1.16+
Meilisearch Search Property search by address, owner name, parcel ID 1.x

3.3 GIS Data ArchitectureΒΆ

graph LR
  subgraph SOURCES["πŸ“‘ Data Sources"]
    SAT["πŸ›°οΈ Satellite\nImagery"]
    SURVEY["πŸ“ Survey\nData"]
    GPS["πŸ“ GPS\nField Collection"]
    LEGACY["πŸ“œ Legacy\nPaper Maps"]
  end

  subgraph INGEST["βš™οΈ Ingestion"]
    QGIS["QGIS\nAuthoring"]
    OCR["OCR Pipeline\nMap Digitization"]
  end

  subgraph STORE["πŸ’Ύ Spatial Storage"]
    POSTGIS["PostGIS\nVector Data"]
    RASTER["MinIO\nRaster Tiles"]
  end

  subgraph SERVE["🌐 Serving"]
    GEOSERVER["GeoServer\nWMS / WFS / WMTS"]
    TILESERV["pg_tileserv\nVector Tiles"]
  end

  subgraph CLIENT["πŸ“± Clients"]
    OPENLAYERS["OpenLayers\nWeb Map"]
    FLUTTER["Flutter\nMobile Map"]
  end

  SOURCES --> INGEST --> STORE --> SERVE --> CLIENT

Spatial Data LayersΒΆ

Layer Geometry Source Update Frequency
Land Parcels Polygon Cadastral survey + GPS On transaction / subdivision
Buildings Polygon (footprint) Survey + satellite On new construction / demolition
Zoning Districts Polygon Master urban plan Annual review
Address Points Point Address registry On registration
Roads & Infrastructure LineString / Polygon Municipality GIS Quarterly
Utility Networks LineString Utility providers On change
Flood Zones / Hazards Polygon Environmental agency Annual

3.4 Core Data Model β€” Property DomainΒΆ

-- Service Catalog (property-specific services)
CREATE TABLE services (
    service_id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    service_code        VARCHAR(50) UNIQUE NOT NULL,
    name_ckb            VARCHAR(300) NOT NULL,
    name_kmr            VARCHAR(300),
    name_ar             VARCHAR(300),
    name_en             VARCHAR(300),
    description_ckb     TEXT,
    description_en      TEXT,
    category            VARCHAR(100), -- registration, transfer, permit, valuation, municipal
    subcategory         VARCHAR(100),
    service_type        VARCHAR(50), -- application, renewal, inquiry, payment, certificate
    channel             VARCHAR(50)[], -- {'online','office','mobile'}
    required_documents  JSONB,
    fee_amount          DECIMAL(12,2) DEFAULT 0,
    fee_currency        VARCHAR(3) DEFAULT 'IQD',
    sla_days            INTEGER,
    is_active           BOOLEAN DEFAULT TRUE,
    form_schema         JSONB,
    workflow_id         UUID,         -- Camunda BPMN workflow
    created_at          TIMESTAMP DEFAULT NOW(),
    updated_at          TIMESTAMP DEFAULT NOW()
);

-- Service Applications (all property service requests)
CREATE TABLE service_applications (
    application_id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    application_number  VARCHAR(30) UNIQUE NOT NULL, -- RE-2026-XXXXXX
    service_id          UUID REFERENCES services(service_id),
    applicant_id        UUID NOT NULL,               -- KRDPASS citizen_id
    property_id         UUID,                        -- Links to properties table
    status              VARCHAR(30) DEFAULT 'submitted',
    priority            VARCHAR(10) DEFAULT 'normal',
    submitted_via       VARCHAR(20),                 -- app, web, office
    form_data           JSONB NOT NULL,
    assigned_to         UUID,
    office_id           UUID,
    notes               TEXT,
    decision            TEXT,
    decided_by          UUID,
    decided_at          TIMESTAMP,
    sla_deadline        TIMESTAMP,
    completed_at        TIMESTAMP,
    created_at          TIMESTAMP DEFAULT NOW(),
    updated_at          TIMESTAMP DEFAULT NOW()
);

-- Payments
CREATE TABLE payments (
    payment_id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    application_id      UUID REFERENCES service_applications(application_id),
    citizen_id          UUID NOT NULL,
    amount              DECIMAL(12,2) NOT NULL,
    currency            VARCHAR(3) DEFAULT 'IQD',
    payment_method      VARCHAR(30),
    payment_gateway     VARCHAR(50), -- FIB, NassPay, Qi, FastPay, AsiaPay
    gateway_txn_id      VARCHAR(200),
    status              VARCHAR(20) DEFAULT 'pending',
    paid_at             TIMESTAMP,
    receipt_url         TEXT,
    created_at          TIMESTAMP DEFAULT NOW()
);

-- Appointments (for in-person services)
CREATE TABLE appointments (
    appointment_id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    citizen_id          UUID NOT NULL,
    service_id          UUID REFERENCES services(service_id),
    office_id           UUID,
    scheduled_date      DATE NOT NULL,
    scheduled_time      TIME NOT NULL,
    duration_minutes    INTEGER DEFAULT 30,
    status              VARCHAR(20) DEFAULT 'booked',
    queue_number        INTEGER,
    check_in_at         TIMESTAMP,
    served_at           TIMESTAMP,
    served_by           UUID,
    created_at          TIMESTAMP DEFAULT NOW()
);

-- Notifications
CREATE TABLE notifications (
    notification_id     UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    citizen_id          UUID NOT NULL,
    channel             VARCHAR(20) NOT NULL, -- sms, push, email, in_app
    title_ckb           VARCHAR(300),
    title_en            VARCHAR(300),
    body_ckb            TEXT,
    body_en             TEXT,
    notification_type   VARCHAR(50), -- status_update, reminder, payment
    reference_type      VARCHAR(50), -- application, appointment, payment
    reference_id        UUID,
    is_read             BOOLEAN DEFAULT FALSE,
    sent_at             TIMESTAMP,
    created_at          TIMESTAMP DEFAULT NOW()
);

Note: Property-specific schemas (parcels, buildings, ownership, permits, valuations) are detailed in 12. Data Models. This section covers the shared service-delivery data model.

-- RBAC Permissions CREATE TABLE roles ( role_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), role_name VARCHAR(100) UNIQUE NOT NULL, description TEXT, permissions JSONB NOT NULL -- ["service.approve","citizen.view","report.generate"] );

CREATE TABLE user_roles ( user_id UUID REFERENCES users(user_id), role_id UUID REFERENCES roles(role_id), assigned_at TIMESTAMP DEFAULT NOW(), assigned_by UUID REFERENCES users(user_id), PRIMARY KEY (user_id, role_id) ); ```