Menyajikan visualisasi data interaktif seperti heatmap densitas relasi (misalnya pemetaan jejaring VC dan startup mirip apparent.social) sering membebani database jika dihitung on-the-fly pada backend runtime. Setiap query agregasi matriks, filtering interaksi, dan kalkulasi bobot node dapat memicu latency tinggi dan kebutuhan infrastruktur server yang mahal.

Solusi yang lebih hemat dan efisien adalah offline pre-computation via Continuous Integration (CI). Melalui pendekatan automasi CI heatmap data, seluruh kalkulasi densitas, validasi format raw data, hingga kompresi artefak dilakukan saat git push. File JSON teroptimasi kemudian dideploy langsung ke CDN atau static site host (Cloudflare Pages, GitHub Pages, atau Vercel) tanpa melibatkan database operasional.

Arsitektur Data Pipeline: Raw JSON ke Edge Static Assets

Pipeline ini memotong dependensi query SQL kompleks di sisi runtime dengan skema kerja statis:

  1. Data Source: File raw JSON relasi (misalnya raw_contacts.json) disimpan dalam Git repository atau diambil dari external bucket via CI runner.
  2. Validasi Skema: Memastikan integritas tipe data dan relasi foreign key via JSON Schema sebelum kalkulasi dimulai.
  3. Transformasi & Agregasi Densitas: Script agregasi membaca interaksi antar-entitas, menghitung koordinat matriks atau tingkat densitas, lalu menyusun snapshot ringkas.
  4. Brotli/Gzip Compression & Publishing: Hasil agregasi disimpan dalam struktur flat JSON, dikompresi, dan dipublikasikan langsung ke CDN.

Validasi Skema Data di CI Menggunakan JSON Schema

Data input yang korup dapat merusak rendering visual heatmap di browser klien. Gunakan pustaka standar seperti ajv (Node.js) atau jsonschema (Python) untuk memutus (fail-fast) workflow CI jika terjadi anomali.

Definisi skema schema.json:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "array",
  "items": {
    "type": "object",
    "required": ["source_id", "target_id", "strength", "timestamp"],
    "properties": {
      "source_id": { "type": "string" },
      "target_id": { "type": "string" },
      "strength": { "type": "number", "minimum": 0, "maximum": 1 },
      "timestamp": { "type": "integer" }
    },
    "additionalProperties": false
  }
}

Script Agregasi Matriks Densitas (Node.js)

Script berikut memvalidasi payload, menghitung densitas interaksi antar-entitas, menyusun bucket data heatmap (2D coordinate binning), lalu mengekspor JSON statis ringkas ke direktori output.

// scripts/aggregate.js
const fs = require('fs');
const path = require('path');
const Ajv = require('ajv');

const rawData = JSON.parse(fs.readFileSync(path.join(__dirname, '../data/raw_contacts.json'), 'utf-8'));
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../data/schema.json'), 'utf-8'));

// 1. Validasi Skema Data
const ajv = new Ajv();
const validate = ajv.compile(schema);
if (!validate(rawData)) {
  console.error('Validation failed:', validate.errors);
  process.exit(1);
}

// 2. Kalkulasi Matriks Densitas
const densityMap = {};
for (const entry of rawData) {
  const pairKey = entry.source_id < entry.target_id 
    ? `${entry.source_id}:${entry.target_id}` 
    : `${entry.target_id}:${entry.source_id}`;

  if (!densityMap[pairKey]) {
    densityMap[pairKey] = { count: 0, totalStrength: 0 };
  }
  densityMap[pairKey].count += 1;
  densityMap[pairKey].totalStrength += entry.strength;
}

// 3. Format Output Siap Render untuk Frontend Canvas/SVG
const output = Object.entries(densityMap).map(([key, value]) => {
  const [source, target] = key.split(':');
  return {
    source,
    target,
    density: Number((value.totalStrength / value.count).toFixed(3)),
    interactions: value.count
  };
});

const outDir = path.join(__dirname, '../public/data');
fs.mkdirSync(outDir, { recursive: true });
fs.writeFileSync(path.join(outDir, 'heatmap.json'), JSON.stringify(output));
console.log(`Berhasil mengagregasi ${output.length} node relasi.`);

Konfigurasi GitHub Actions Pipeline

Workflow berikut menjalankan proses validasi, kalkulasi densitas, kompresi file via Gzip, dan penyimpanan artefak static build.

name: Build Heatmap Data

on:
  push:
    branches: [main]
    paths:
      - 'data/**'
      - 'scripts/**'
  workflow_dispatch:

jobs:
  build-data:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install Dependencies
        run: npm ci

      - name: Validate & Aggregate Data
        run: node scripts/aggregate.js

      - name: Pre-compress Artifacts
        run: |
          gzip -k -9 public/data/heatmap.json

      - name: Upload Static Data Artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: 'public/'

  deploy:
    needs: build-data
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

Trade-offs dan Pertimbangan Teknis

Meskipun pendekatan ini meniadakan biaya server database dan mempercepat render hingga ratusan milidetik melalui edge cache, beberapa batas operasional harus diperhatikan:

  • Eventual Latency: Pembaruan data tidak terjadi secara real-time. Ada jeda eksekusi job CI (umumnya 30 detik hingga 2 menit) dari trigger git push sampai static asset terdistribusi.
  • Payload Footprint: Jika dataset relasional menghasilkan puluhan ribu relasi aktif, JSON dapat membengkak. Terapkan sparse matrix pruning (menyingkirkan bobot interaksi 0 atau di bawah ambang batas visual).
  • Chunking Strategi: Untuk ukuran dataset besar, jangan ekspor satu file heatmap.json raksasa. Pisahkan data berdasarkan kluster kategori atau viewport tile (misalnya public/data/tile-1-2.json) agar browser mengunduh data secara on-demand.