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) ); ```