Setiap gudang data yang gagal hampir selalu gagal pada tahap yang sama: tim data langsung membuat tabel tanpa mengetahui pertanyaan apa yang harus dijawab. Pada bagian ini kita membalik urutan tersebut dengan menuliskan kebutuhan analitik TokoKita terlebih dahulu, lalu menurunkannya menjadi keputusan grain dan definisi metrik. Hasil bagian ini adalah satu dokumen definisi metrik pada models/metric_definitions.yaml yang akan menjadi kontrak bagi seluruh bagian berikutnya. Materi pendukung ada pada chapter Dimensional Modeling dan Grain dalam Data Model.

Pertanyaan Bisnis yang Harus Dijawab #

Tim manajemen TokoKita memberi enam pertanyaan yang harus dijawab gudang data ini setiap bulan. Kita catat beserta sumbu analisis dan pengukuran yang dibutuhkan tiap pertanyaan:

Pertanyaan bisnisSumbu (dimensi)Pengukuran (measure)
Berapa GMV per kota pelanggan tiap bulan?waktu, kota pelanggantotal penjualan
Produk kategori mana yang paling banyak terjual?kategori produkjumlah item
Seberapa besar kontribusi gerai online dibanding offline?tipe tokototal penjualan
Berapa rata-rata nilai satu pesanan?waktutotal, jumlah pesanan
Berapa porsi pesanan yang dibatalkan?waktu, tokojumlah pesanan
Apakah pelanggan yang berpindah kota tetap membeli setelah pindah?pelanggan, waktu, kotajumlah pesanan

Dua hal langsung terlihat dari tabel di atas. Pertama, seluruh pertanyaan memotong data menurut waktu sehingga dimensi tanggal wajib ada. Kedua, pertanyaan terakhir menuntut riwayat atribut pelanggan, bukan sekadar kondisi terkini, sehingga kita tahu sejak awal bahwa dim_pelanggan perlu Slowly Changing Dimension Type 2.

Menetapkan Grain #

Grain adalah pernyataan satu baris fact table mewakili apa. Kita menetapkan grain sebagai berikut:

Satu baris fact_pesanan mewakili satu baris pesanan: satu produk dengan kuantitas tertentu di dalam satu pesanan.

Pesanan ORD-0004 yang berisi dua produk akan menempati dua baris fact table. Tiga alternatif grain pernah kita pertimbangkan dan masing-masing ditolak dengan alasan jelas:

  • satu baris per pesanan — menghalangi analisis per produk karena beberapa produk berbeda harus digabung ke satu baris,
  • satu baris per pengiriman — data sumber tidak mencatat pengiriman, hanya baris pesanan,
  • satu baris per harian per toko — agregat harian menghalangi penghitungan ulang metrik baru di masa depan karena detail transaksi sudah dibuang.

Prinsipnya seperti pada chapter Grain dalam Data Model: pilih grain paling halus yang masih bisa menjawab seluruh pertanyaan bisnis, karena agregasi dari detail selalu mungkin tetapi pemecahan agregat kembali menjadi detail mustahil dilakukan.

Konsekuensi grain ini kita catat juga untuk dimensi. Karena satu baris fact adalah satu baris pesanan, dimensi pelanggan, produk, dan toko hanya perlu berada pada grain entitas biasa, sedangkan dimensi tanggal cukup harian. Tidak ada dimensi tambahan yang dibutuhkan untuk menjawab keenam pertanyaan.

Menetapkan Kosakata Status #

Sumber data memakai tiga status pesanan dalam Bahasa Indonesia yang kita pertahankan konsisten dengan section Data Engineering untuk Analytics: selesai, dibatalkan, dan dikembalikan. GMV hanya menghitung pesanan berstatus selesai; pesanan dikembalikan dicatat sebagai transaksi dengan status khusus, bukan dihapus, agar analisis retur tetap mungkin. Keputusan ini ditulis eksplisit pada berkas definisi metrik supaya tidak ada perdebatan di kemudian hari.

Mendefinisikan Metrik #

Definisi metrik kita simpan sebagai berkas YAML pada models/metric_definitions.yaml agar dapat dibaca manusia sekaligus diproses program. Pendekatan ini meniru semantic model dan metrics pada dbt yang telah kita bahas pada chapter Semantic Layer:

yaml
# Definisi metrik gudang data penjualan TokoKita.
# Satu sumber kebenaran bagi GMV dan metrik turunannya; dbt memiliki fasilitas
# setara berupa semantic model + metrics (lihat Chapter Semantic Layer).
versi: 1
model: fact_pesanan
status_valid: [selesai, dibatalkan, dikembalikan]
metrik:
  - nama: gmv
    label: Gross Merchandise Value
    deskripsi: Nilai penjualan pesanan berstatus selesai
    ekspresi: SUM(total)
    filter_status: [selesai]
    satuan: rupiah
  - nama: jumlah_item_terjual
    deskripsi: Banyaknya item terjual pada pesanan berstatus selesai
    ekspresi: SUM(jumlah)
    filter_status: [selesai]
    satuan: item
  - nama: total_diskon
    deskripsi: Akumulasi diskon pada pesanan berstatus selesai
    ekspresi: SUM(diskon)
    filter_status: [selesai]
    satuan: rupiah
  - nama: jumlah_pesanan
    deskripsi: Banyaknya pesanan unik berstatus selesai
    ekspresi: COUNT(DISTINCT pesanan_id)
    filter_status: [selesai]
    satuan: pesanan

Cuplikan di atas memuat empat dari enam entri; dua entri terakhir pada berkas, nilai_pesanan_rata_rata dan tingkat_pembatalan, dipangkas karena berbentuk turunan. Mari kita telaah struktur setiap entri:

  1. nama dan label: pengenal metrik; label dipakai saat metrik ditampilkan pada laporan.
  2. ekspresi: agregasi SQL atas kolom fact_pesanan; uji end-to-end pada Bagian 7 memeriksa setiap kolom yang disebut ekspresi benar-benar ada pada fact table.
  3. filter_status: filter status yang melekat pada definisi, sehingga GMV tidak mungkin dihitung dengan cara berbeda oleh dua orang analis.
  4. turunan dan rumus: pada bagian berkas yang dipangkas, metrik seperti nilai_pesanan_rata_rata tidak punya ekspresi SQL sendiri, melainkan rumus atas metrik lain, mirip derived metric pada dbt.

Kita jalankan pemeriksaan struktur berkas tersebut sekarang juga agar kesalahan ketik terdeteksi sejak awal:

python
import yaml

definisi = yaml.safe_load(open("models/metric_definitions.yaml", encoding="utf-8"))
print(f"model sumber   : {definisi['model']}")
print(f"status valid   : {definisi['status_valid']}")
print(f"jumlah metrik  : {len(definisi['metrik'])}")
for metrik in definisi["metrik"]:
    ekspresi = metrik.get("ekspresi", metrik.get("rumus", "?"))
    print(f"  - {metrik['nama']:28s} {ekspresi}")
assert definisi["model"] == "fact_pesanan"
assert len(definisi["metrik"]) == 6
print("validasi struktur: lulus")

Output yang Dihasilkan:

text
model sumber   : fact_pesanan
status valid   : ['selesai', 'dibatalkan', 'dikembalikan']
jumlah metrik  : 6
  - gmv                          SUM(total)
  - jumlah_item_terjual          SUM(jumlah)
  - total_diskon                 SUM(diskon)
  - jumlah_pesanan               COUNT(DISTINCT pesanan_id)
  - nilai_pesanan_rata_rata      gmv / jumlah_pesanan
  - tingkat_pembatalan           COUNT(DISTINCT pesanan_id)
validasi struktur: lulus

Daftar Periksa Kebutuhan #

Sebelum lanjut, kita pastikan enam keputusan bagian ini sudah mengikat:

  • enam pertanyaan bisnis tercatat beserta sumbu dan pengukurannya,
  • grain ditetapkan pada satu baris pesanan,
  • dimensi yang dibutuhkan: pelanggan, produk, tanggal, toko,
  • dim_pelanggan membutuhkan riwayat SCD Type 2,
  • kosakata status: selesai, dibatalkan, dikembalikan, dan
  • enam metrik terdefinisi pada models/metric_definitions.yaml.

Dengan kebutuhan yang sudah mengikat, kita siap menggambar star schema yang menjawab seluruh pertanyaan tersebut pada Bagian 2.