Desain proyek

Merancang proyek pipeline, yaitu pemilihan sumber nyata, target termodel, dan SLA.

Daftar isi

Desain proyek menetapkan arah pengerjaan sebelum satu baris pipeline ditulis, yaitu sumber data yang dipakai, bentuk targetnya, dan tingkat layanan yang dijanjikan. Topik ini menjalankan tahap tersebut untuk proyek peminjaman perpustakaan kota, satu proyek yang dipakai berulang pada seluruh modul. Hasil akhirnya berupa berkas rencana yang kelengkapannya dapat diperiksa secara otomatis, sehingga keputusan desain tidak tinggal sebagai kesepakatan lisan.

Memilih sumber data yang nyata#

Sumber data proyek dipilih dari data yang benar-benar ada dan dapat diakses, bukan dari tabel karangan yang selalu rapi. Kriterianya ada tiga, yaitu sumber dapat ditarik secara berkala, volumenya cukup untuk latihan query, dan datanya memuat cacat nyata seperti nilai kosong. Cacat itu justru menguntungkan karena validasi pada tahap pembangunan perlu diuji dengan kasus yang sungguh terjadi. Sumber yang hanya dipahami dari deskripsi biasanya menyembunyikan masalah skema yang baru terlihat saat ekstraksi.

Proyek peminjaman perpustakaan memakai dua sumber. Sumber pertama adalah ekspor CSV dari sistem perpustakaan kota yang berisi kolom id_pinjam, tanggal_pinjam, tanggal_kembali, id_anggota, judul_buku, dan kategori. Sebagian tanggal_kembali sengaja kosong karena pinjamannya masih berjalan, sehingga pipeline harus menangani nilai kosong sejak awal. Sumber kedua adalah layanan anggota internal yang pada pekerjaan nyata berupa API, sedangkan pada contoh ini diwakili berkas JSON berisi lima anggota dengan id, nama, dan kelompoknya.

Kedua sumber disimpan sebagai berkas statis bersama artikel ini agar pipeline dapat diuji ulang dengan data yang sama persis. Pada pekerjaan nyata, ekspor CSV ditarik dari folder bersama sistem perpustakaan dan JSON diunduh dari API internal. Simpan contoh sumber sejak tahap desain karena berkas inilah yang menentukan bentuk validasi dan transformasi di kemudian hari.

Merumuskan target termodel#

Target termodel adalah tabel dengan bentuk dan fungsi yang sudah ditetapkan, bukan salinan mentah sumber yang ditumpuk begitu saja. Bentuknya diturunkan dari pertanyaan analisis yang harus dijawab, bukan dari selera pemodel. Untuk proyek ini, pertanyaannya ada tiga, yaitu berapa jumlah peminjaman per bulan, berapa rata-rata durasi pinjam per kategori buku, dan seberapa aktif anggota pada tiap kelompok.

Pertanyaan agregasi semacam itu dijawab paling mudah dengan star schema, yaitu pola pemodelan yang menempatkan satu tabel fakta di tengah dan beberapa tabel dimensi di sekelilingnya. Karena itu, target proyek ditetapkan sebagai fakta_peminjaman beserta dim_buku dan dim_anggota. Dua tabel staging mendahuluinya, yaitu tabel jeda penyimpanan data apa adanya yang bernama peminjaman_mentah dan anggota_mentah. Tabel staging menyimpan data mentah, sedangkan tabel dimensi dan fakta sudah bersih dan berelasi.

Alternatif pola lain adalah medallion, yakni pelapisan bronze, silver, dan gold untuk memisahkan data mentah, data bersih, dan data siap pakai. Pola medallion lebih tepat bila sumbernya banyak dan saling berbeda, sedangkan proyek kecil dengan dua sumber cukup dilayani star schema ditambah staging. Pilihan yang lebih kecil lebih cepat dibangun dan lebih mudah dijelaskan saat demonstrasi proyek.

Arsitektur proyek#

Arsitektur proyek memetakan aliran data dari sumber sampai target beserta tanggung jawab tiap tahap, sehingga setiap bagian pipeline punya tempat yang jelas. Diagram juga menjadi alat komunikasi dengan peninjau proyek karena alur yang benar lebih mudah diperiksa dalam gambar daripada dalam prosa panjang. Diagram berikut menggambarkan arsitektur proyek peminjaman dari dua sumber sampai tiga tabel target.

Ekspor CSV peminjamanAPI anggota (JSON)Ekstraksi dan validasi (Python)Transformasi SQL (DuckDB)Muat harian sebelum 07:00fakta_peminjaman, dim_buku, dim_anggota
Arsitektur proyek peminjaman perpustakaan dari sumber ke target

Aliran data bergerak dari dua sumber menuju ekstraksi dan validasi yang ditulis dengan Python, dilanjutkan transformasi SQL dengan DuckDB, dan berakhir pada muat harian ke tabel target. Basis data DuckDB tunggal berperan sebagai warehouse, yaitu gudang data analitik, sehingga tabel staging, dimensi, dan fakta berada di satu tempat. Penempatan yang sederhana ini disengaja karena proyek portofolio harus dapat dijalankan ulang di mesin apa pun tanpa menyiapkan server. Apabila volume data membesar, pola tahapannya tetap berlaku dan yang berganti hanyalah perangkat penyimpanannya.

Menetapkan SLA#

SLA atau service level agreement adalah janji terukur atas kinerja pipeline, dan janji inilah yang membuat kata siap memiliki arti yang dapat diperiksa. SLA proyek ini menetapkan tiga hal, yaitu kesegaran harian dengan tenggat muat pukul 07:00, kriteria sukses berupa semua aserasi validasi lulus, dan batas kesegaran maksimum 24 jam sejak ekspor sumber terakhir. Tenggat pukul 07:00 dipilih sebagai contoh karena laporan perpustakaan dibaca petugas pada awal hari kerja.

Kesegaran (freshness), yaitu selisih waktu antara data terakhir yang berhasil dimuat dan saat pemeriksaan, menjadi metrik yang dijanjikan oleh SLA tersebut. Setiap janji harus disertai cara mengukurnya. Kesegaran dihitung dari tanggal run terakhir yang lulus pada tabel status run, sedangkan kriteria sukses dihitung dari lulusnya aserasi yang ditanam di pipeline. Angka pada SLA dipilih agar terukur dan terverifikasi, bukan untuk menarik pengguna, karena janji yang tidak dapat diperiksa hanya menimbulkan sengketa saat kegagalan.

Mendokumentasikan keputusan dalam rencana#

Seluruh keputusan desain dituangkan dalam satu berkas rencana supaya dapat dibaca ulang, dibandingkan antarproyek, dan diperiksa kelengkapannya secara otomatis. Berkas rencana.yaml berikut menetapkan proyek, tujuan, sumber, target, dan SLA dalam bentuk yang ringkas. Bentuk YAML dipilih karena tetap terbaca manusia sekaligus mudah dimuat oleh program.

yaml rencana.yaml
# Rencana desain proyek pipeline peminjaman perpustakaan kota.
# Berkas ini menjadi acuan pembangunan pada topik berikutnya.

proyek: peminjaman-perpustakaan
tujuan: laporan peminjaman harian dan bulanan untuk pengelola perpustakaan

sumber:
  - nama: ekspor CSV peminjaman
    jenis: berkas CSV
    lokasi: peminjaman.csv
  - nama: layanan anggota internal
    jenis: API JSON
    lokasi: anggota.json

target:
  model: star schema
  tabel:
    - fakta_peminjaman
    - dim_buku
    - dim_anggota

sla:
  kesegaran_maksimum_jam: 24
  tenggat_muat: "07:00"
  kriteria_sukses: semua aserasi validasi lulus

Skrip perancangan.py membaca berkas tersebut, memvalidasi bagian wajibnya dengan aserasi, lalu mencetak ringkasan keputusan desain. Kelengkapan yang diperiksa mencakup keberadaan lima bagian utama, atribut nama, jenis, dan lokasi pada setiap sumber, serta tiga atribut SLA. Pemeriksaan semacam ini mencegah rencana yang tampak lengkap tetapi sebenarnya kehilangan bagian penting, misalnya sumber tanpa lokasi yang jelas.

python perancangan.py
"""Pemeriksa kelengkapan rencana desain proyek peminjaman perpustakaan.

Skrip membaca rencana.yaml, memvalidasi bagian wajib, lalu mencetak
ringkasan keputusan desain. Jalankan dari dalam direktori topik:
python3 perancangan.py
"""

from pathlib import Path

import yaml

RENCANA = Path("rencana.yaml")

# bagian wajib beserta tipe nilainya
WAJIB = {
    "proyek": str,
    "tujuan": str,
    "sumber": list,
    "target": dict,
    "sla": dict,
}
KUNCI_SUMBER = {"nama", "jenis", "lokasi"}
KUNCI_TARGET = {"model", "tabel"}
KUNCI_SLA = {"kesegaran_maksimum_jam", "tenggat_muat", "kriteria_sukses"}


def validasi(rencana: dict) -> None:
    """Asersasi kelengkapan rencana sebelum desain disetujui."""
    assert RENCANA.exists(), f"{RENCANA} tidak ditemukan"
    for kunci, tipe in WAJIB.items():
        assert kunci in rencana, f"bagian `{kunci}` belum ada di rencana"
        assert isinstance(rencana[kunci], tipe), f"bagian `{kunci}` salah tipe"
    for sumber in rencana["sumber"]:
        kurang = KUNCI_SUMBER - set(sumber)
        assert not kurang, f"sumber `{sumber.get('nama', '?')}` kurang {sorted(kurang)}"
    for kunci in KUNCI_TARGET:
        assert kunci in rencana["target"], f"bagian target kurang `{kunci}`"
    for kunci in KUNCI_SLA:
        assert kunci in rencana["sla"], f"bagian sla kurang `{kunci}`"


def ringkasan(rencana: dict) -> str:
    """Susun ringkasan keputusan desain dalam beberapa baris."""
    nama_sumber = ", ".join(s["nama"] for s in rencana["sumber"])
    tabel = ", ".join(rencana["target"]["tabel"])
    sla = rencana["sla"]
    return "\n".join(
        [
            f"proyek          : {rencana['proyek']}",
            f"tujuan          : {rencana['tujuan']}",
            f"sumber          : {len(rencana['sumber'])} ({nama_sumber})",
            f"model target    : {rencana['target']['model']} dengan {len(rencana['target']['tabel'])} tabel",
            f"tabel target    : {tabel}",
            f"SLA             : kesegaran maksimum {sla['kesegaran_maksimum_jam']} jam, muat sebelum {sla['tenggat_muat']}",
            f"kriteria sukses : {sla['kriteria_sukses']}",
        ]
    )


def main() -> None:
    rencana = yaml.safe_load(RENCANA.read_text(encoding="utf-8"))
    validasi(rencana)
    print("rencana valid")
    print(ringkasan(rencana))


if __name__ == "__main__":
    main()
text Keluaran
rencana valid
proyek          : peminjaman-perpustakaan
tujuan          : laporan peminjaman harian dan bulanan untuk pengelola perpustakaan
sumber          : 2 (ekspor CSV peminjaman, layanan anggota internal)
model target    : star schema dengan 3 tabel
tabel target    : fakta_peminjaman, dim_buku, dim_anggota
SLA             : kesegaran maksimum 24 jam, muat sebelum 07:00
kriteria sukses : semua aserasi validasi lulus

Skrip dijalankan dengan perintah python3 perancangan.py dari dalam direktori topik. Apabila satu bagian dihapus dari rencana.yaml, aserasi berhenti dengan pesan yang menyebut bagian yang kurang dan ringkasan tidak dicetak. Rencana yang lulus pemeriksaan menjadi acuan tetap bagi tahap pembangunan, sehingga perubahan desain di kemudian hari wajib lewat suntingan berkas yang sama.

Kesalahan umum#

Kesalahan desain baru terasa pada tahap pembangunan, padahal perbaikannya jauh lebih murah dilakukan saat rencana masih berupa berkas kecil. Empat kesalahan berikut paling sering ditemui. Semuanya dicegah dengan satu kebiasaan yang sama, yaitu membuktikan asumsi dengan berkas nyata.

  • Menulis pipeline untuk data yang dibayangkan rapi. Sumber yang belum pernah dilihat aslinya sering memuat nilai kosong atau id ganda yang mematahkan aserasi. Simpan contoh sumber sejak tahap desain dan baca isinya sebelum menetapkan skema target.
  • Menetapkan target tanpa pertanyaan analisis. Tabel yang dibangun tanpa pola pertanyaan mudah membengkak dan sulit dijustifikasi. Turunkan setiap tabel target dari daftar pertanyaan yang tertulis di rencana.
  • Menjanjikan SLA tanpa metrik. Janji secepat mungkin tidak dapat diverifikasi saat insiden. Ganti setiap janji dengan angka dan cara ukurnya, misalnya kesegaran maksimum 24 jam yang dihitung dari tabel status run.
  • Menyimpan rencana hanya di luar proyek. Dokumen desain yang terpisah dari kode cepat ketinggalan saat keputusan berubah. Simpan rencana.yaml di dalam proyek dan perbarui bersama perubahan kodenya.

Ringkasan#

  • Sumber proyek dipilih dari data nyata yang memuat cacat, yaitu ekspor CSV peminjaman dan layanan anggota yang diwakili berkas JSON.
  • Target diturunkan dari pertanyaan analisis, berupa star schema kecil fakta_peminjaman, dim_buku, dan dim_anggota dengan dua tabel staging.
  • SLA menetapkan kesegaran harian sebelum pukul 07:00 beserta cara mengukurnya melalui tabel status run.
  • Seluruh keputusan disimpan di rencana.yaml yang kelengkapannya diperiksa perancangan.py secara otomatis.

Referensi#