Topik pesanan-tokokita ditulis oleh tim kasir dan dibaca oleh tiga pipeline lain — laporan harian, deteksi penipuan, dan rekomendasi. Suatu hari tim kasir mengganti field harga menjadi price demi konsistensi dengan sistem baru. Pesan tetap terkirim, consumer tetap membaca, tetapi field harga yang dibaca pipeline lama kini kosong: agregat pendapatan melandai tanpa satu pun pesan galat. Schema registry hadir untuk mencegah kejadian semacam itu: satu pusat yang menyimpan definisi bentuk data, memeriksa setiap perubahan, dan menolak evolusi yang merusak pembaca. Chapter ini membedah mekanismenya untuk event berformat Avro di atas Kafka.
Skema Avro untuk Event Pesanan #
Avro mendefinisikan bentuk data lewat skema JSON, lalu menyimpan payload sebagai biner ringkas. Skema pesanan TokoKita di bawah memuat lima field wajib dan satu field baru voucher yang sengaja dibuat opsional dengan nilai bawaan null — detail kecil yang menentukan boleh-tidaknya perubahan tersebut:
{
"type": "record",
"name": "Pesanan",
"namespace": "id.tokokita.pesanan",
"fields": [
{"name": "pesanan_id", "type": "string"},
{"name": "pelanggan", "type": "string"},
{"name": "produk", "type": "string"},
{"name": "qty", "type": "int"},
{"name": "harga", "type": "int"},
{"name": "voucher", "type": ["null", "string"], "default": null}
]
}Skema versi baru yang menambahkan voucher ini aman bagi consumer lama maupun baru. Consumer lama yang belum mengenal voucher cukup mengabaikan field tersebut, sedangkan consumer baru yang membaca data lama mendapat null dari nilai bawaan. Inilah yang dimaksud evolusi skema: definisi berubah tanpa memaksa semua pembaca berubah pada saat yang sama.
Mode Kompatibilitas #
Setiap subject di registry — biasa dinamai <topic>-value — menyimpan deretan versi skema. Sebelum menerima versi baru, registry memeriksa kompatibilitasnya terhadap versi terakhir sesuai kebijakan yang dipilih per subject:
| Mode | Pemeriksaan | Perubahan Avro yang lolos |
|---|---|---|
| BACKWARD | skema baru membaca data lama | tambah field ber-default, hapus field |
| FORWARD | skema lama membaca data baru | tambah field, hapus field ber-default |
| FULL | keduanya sekaligus | tambah atau hapus field, asal ber-default |
| NONE | tanpa pemeriksaan | semua perubahan diterima |
BACKWARD adalah pilihan paling umum karena melindungi kelompok terbesar: seluruh consumer yang membaca topic. FORWARD melindungi arah sebaliknya — data baru tetap terbaca oleh consumer lama yang belum diperbarui. FULL memberi kebebasan penuh untuk memutakhirkan producer dan consumer secara terpisah, dengan konsekuensi setiap field baru atau yang dihapus wajib punya nilai bawaan.
Skenario Evolusi yang Aman dan yang Merusak #
Dua perubahan pada skema pesanan memberi contoh kontras. Pertama, penambahan voucher dengan "default": null lolos pemeriksaan BACKWARD: registry menerima versi baru, producer mulai mengirimnya, dan consumer lama tetap hidup. Kedua, penggantian nama harga menjadi price ditolak dengan respons 409 Conflict — bagi Avro, rename adalah hapus field harga tambah field price, dan field baru tanpa nilai bawaan membuat skema baru gagal membaca data lama.
Penolakan itulah inti nilai registry: kegagalan terjadi saat deploy producer, bukan diam-diam di produksi. Dua jalan keluar tersedia untuk rename — menambahkan "aliases": ["harga"] pada field price agar pembaca lama tetap menemukan datanya, atau menambahkan field baru dahulu, memigrasi semua pembaca, lalu menghapus field lama pada versi berikutnya.
Alur Registrasi dan Serialisasi #
Registry tidak berada di jalur data — Kafka tetap menerima bytes apa pun. Peran registry terjadi di sekitar produksi dan konsumsi pesan:
Producer mendaftarkan skema sekali per versi dan menerima id kecil — pada diagram di atas, 42. Setiap pesan kemudian dikirim dengan penanda format dan id tersebut di awal payload. Consumer membaca id, mengambil definisi skema dari registry (lalu menyimpannya di cache), dan melakukan deserialize dengan definisi yang persis sama seperti saat pesan ditulis — bukan definisi terbaru yang mungkin sudah berubah.
Kode Klien dan Pilihan Format #
Sketsa berikut memperlihatkan panggilan REST yang dilakukan producer saat mendaftarkan skema. Registry di lingkungan penulisan tidak terpasang, sehingga sketsa diverifikasi sintaks saja dan sengaja tidak disertai blok output:
"""Sketsa klien schema registry via REST (diverifikasi sintaks, tidak dieksekusi).
Alur: daftarkan skema -> terima id -> id disertakan pada setiap pesan.
Di produksi, langkah ini dijalankan otomatis oleh serializer pustaka klien
(misalnya confluent-kafka), lengkap dengan cache skema, bukan manual begini.
"""
import json
import requests
REGISTRY = "http://localhost:8081"
SUBJECT = "pesanan-value"
SKEMA_PESANAN = {
"type": "record",
"name": "Pesanan",
"namespace": "id.tokokita.pesanan",
"fields": [
{"name": "pesanan_id", "type": "string"},
{"name": "pelanggan", "type": "string"},
{"name": "produk", "type": "string"},
{"name": "qty", "type": "int"},
{"name": "harga", "type": "int"},
{"name": "voucher", "type": ["null", "string"], "default": None},
],
}
def id_skema() -> int:
"""Daftarkan skema dan kembalikan id-nya.
Registry menjawab 200 dengan id bila skema baru kompatibel,
409 bila melanggar kebijakan kompatibilitas subject.
"""
r = requests.post(
f"{REGISTRY}/subjects/{SUBJECT}/versions",
json={"schemaType": "AVRO", "schema": json.dumps(SKEMA_PESANAN)},
timeout=10,
)
if r.status_code == 409:
raise RuntimeError("skema ditolak: melanggar kebijakan kompatibilitas")
r.raise_for_status()
return r.json()["id"]Consumer melakukan langkah kebalikannya lewat GET /schemas/ids/42, biasanya hanya sekali sebelum hasilnya di-cache. Avro dipakai di sini karena paling lazim di ekosistem Kafka, tetapi registry yang sama juga melayani JSON Schema — yang tetap terbaca manusia — dan Protobuf, yang umum di lingkungan yang sudah memakai gRPC. Spesifikasi lengkap mode kompatibilitas dan API REST terdokumentasi di dokumentasi Confluent Schema Registry
; rujukan bahasa skemanya di dokumentasi Apache Avro
.
Dengan bentuk data terjaga kontraknya, stream pesanan siap diolah secara berkelanjutan. Pertanyaan berikutnya bergeser ke engine yang memprosesnya: bagaimana state agregasi dipelihara, dipulihkan setelah gagal, dan ditulis ke hilir dengan aman — pokok bahasan Chapter Stream Processing dengan Spark.