Protocol Buffers (protobuf) adalah salah satu serialization format yang sangat powerful dan banyak digunakan dalam pengembangan sistem terdistribusi atau aplikasi yang membutuhkan komunikasi antar service. Di balik efisiensi dan fleksibilitasnya, salah satu fitur yang sering menjadi pertanyaan bagi engineer baru maupun senior adalah nested messages—atau pesan bersarang.
Pada artikel ke-41 dalam serial Protocol Buffers ini, saya akan membedah konsep nested messages mulai dari definisi, implementasi, hingga best practices yang saya temukan selama bertahun-tahun mengelola data model dengan protobuf. Kita akan menyimak aturannya, memecahkan edge-cases, dan menguji validitas serta fleksibilitas nested message melalui contoh kode, diagram alur, dan simulasi nyata. Mari kita mulai!
Apa Itu Nested Messages?
Secara sederhana, nested message dalam protobuf adalah sebuah message
yang didefinisikan di dalam message lain. Ini dapat dianalogikan seperti inner class pada Java atau struct dalam struct di C. Nested message sangat berguna untuk mengelompokkan data yang berhubungan erat dan menghindari pencemaran namespace global.
Struktur Dasar
Mari kita lihat contoh dasar berikut:
syntax = "proto3";
message AddressBook {
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
repeated Person people = 1;
}
Penjelasan:
AddressBook
adalah message utama.Person
adalah pesan bersarang di dalamAddressBook
.- Field
people
akan mengandung daftar objekPerson
.
Kapan Menggunakan Nested Messages?
Berdasarkan pengalaman, nested message sangat bermanfaat ketika:
- Hubungan data bersifat hirarkis/menuju ke satu arah.
- Struktur pesan tidak digunakan secara global atau lintas file.
- Ingin mengorganisasi tipe data yang hanya relevan pada message tertentu.
Tabel: Perbandingan Penggunaan Nested dan Top-level Messages
Skenario | Nested Message | Top-level Message |
---|---|---|
Struktur Data Hirarkis | ✅ | ✅ |
Reusabilitas Tinggi Lintas File | ❌ | ✅ |
Scope Lokal pada Message | ✅ | ❌ |
Mengakses Nested Messages di Kode
Generate File Protobuf
protoc --go_out=. --go-grpc_out=. addressbook.proto
Implementasi nested message otomatis akan menghasilkan inner class (di Java/Python), atau namespace bertingkat di Go/C++. Berikut contohnya:
Java
AddressBook.Person person = AddressBook.Person.newBuilder()
.setName("Luna")
.setId(1)
.setEmail("luna@example.com")
.build();
Go
person := &pb.AddressBook_Person{
Name: "Luna",
Id: 1,
Email: "luna@example.com",
}
Catatan: Lihat bagaimana di Go, nested message menghasilkan tipe dengan nama Parent_Child
(AddressBook_Person
).
Simulasi: Serialisasi dan Deserialisasi Nested Messages
Mari kita coba simulasi sederhana data serialization-deserialization menggunakan Python.
1. Protofile: addressbook.proto
syntax = "proto3";
package example;
message AddressBook {
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
repeated Person people = 1;
}
2. Generate File Go dari Proto
Jalankan perintah berikut untuk menghasilkan file Go (pastikan sudah install protoc
dan plugin Go):
protoc --go_out=. addressbook.proto
Output: addressbook.pb.go
3. Kode Go untuk Serialize & Deserialize
package main
import (
"fmt"
"log"
"google.golang.org/protobuf/proto"
// Ganti sesuai dengan path package hasil generate proto Anda
examplepb "path/to/your/generated/example"
)
func main() {
// Buat AddressBook dan tambahkan 1 Person
book := &examplepb.AddressBook{
People: []*examplepb.AddressBook_Person{
{
Name: "Lenka",
Id: 42,
Email: "lenka@medium.com",
},
},
}
// Serialize ke []byte
data, err := proto.Marshal(book)
if err != nil {
log.Fatalf("Gagal serialisasi: %v", err)
}
// Deserialize dari []byte
parsed := &examplepb.AddressBook{}
if err := proto.Unmarshal(data, parsed); err != nil {
log.Fatalf("Gagal parsing: %v", err)
}
// Cetak hasilnya
for _, person := range parsed.People {
fmt.Println("Name :", person.Name)
fmt.Println("ID :", person.Id)
fmt.Println("Email:", person.Email)
}
}
🧾 Output:
Name : Lenka
ID : 42
Email: lenka@medium.com
✅ Catatan:
- Ganti
examplepb
dengan path Go module dari hasil generateaddressbook.pb.go
. - Gunakan module path seperti
github.com/username/project/proto/example
bila memakai Go module.
Diagram Alur: Proses Serialization Nested Messages
Kita dapat memvisualisasikan proses serialization dan deserialization nested message dengan diagram berikut (menggunakan Mermaid):
flowchart LR A[Create AddressBook] --> B[Add Person] B --> C[Set Attributes] C --> D[Serialize To Bytes] D --> E[Deserialize From Bytes] E --> F[Read Nested Person Data]
Edge Cases dan Trik Praktis
Berikut beberapa edge cases dan best practices seputar nested messages yang sering saya temui:
1. Forward Reference Tidak Diizinkan
Nested message tidak dapat mereferensikan parent message secara langsung karena mereka belum didefinisikan saat parsing. Gunakan field tipe lain atau refactor message jika dependencies dua arah dibutuhkan.
2. Penggunaan Repeated Nested
Masukkan repeated field untuk array of nested messages:
repeated Person people = 1;
3. Nested Lebih Dalam? Hati-hati!
Protobuf memang mendukung bersarang hingga level yang dalam, namun terlalu dalam akan menurunkan keterbacaan dan meningkatkan kompleksitas serialization. Rekomendasi saya: maksimal dua tingkat jika memungkinkan.
4. Reuse Nested Message di Luar Parent
Jika Anda harus menggunakan type yang sama di tempat lain, pindahkan deklarasi nested message ke level atas agar bisa diimport dan direferensikan message lain.
Kelebihan & Kekurangan Nested Message
Kelebihan | Kekurangan |
---|---|
Namespace lokal, mengurangi tabrakan | Tidak dapat di-reuse lintas file/message |
Struktur data lebih jelas dan rapi | Sulit di-maintain jika kedalaman bersarang |
Tipe hanya terkait parent | Dependencies bisa menjadi tidak jelas |
Studi Kasus Sederhana: Log Pembayaran
Anggap kita membangun sistem log pembayaran:
message PaymentLog {
string transaction_id = 1;
message ItemDetail {
string item_id = 1;
int32 quantity = 2;
}
repeated ItemDetail items = 2;
double total = 3;
}
Dengan desain ini, tipe ItemDetail
hanya dapat digunakan dalam konteks transaksi pembayaran, dan tidak akan clash ketika Anda memiliki message serupa dengan field sama di context lain.
Kesimpulan
Nested message adalah fitur powerful dari Protocol Buffers yang membantu kita menjaga struktur dan modularitas data model. Kuncinya adalah memahami konteks penggunaan dan potensi implikasi teknisnya. Sebagai engineer, gunakan nested message untuk:
- Mengorganisasi tipe data.
- Mencegah pollution pada global namespace.
- Membatasi scope dari tipe yang tidak perlu diekspos keluar.
Namun, berhati-hatilah terhadap reusability dan maintainability! Gunakan nested message dengan bijak sesuai dengan prinsip encapsulation dan separation of concern.
Praktik terbaik: Nest when only local, flatten when reused.
Kita sudah menelusuri teori, simulasi praktik, hingga kecenderungan yang saya temui di lapangan. Jika Anda punya pengalaman atau pertanyaan seputar nested messages di protobuf, silakan share di kolom komentar! 🚀
Referensi Tambahan
Terima kasih telah membaca. Jangan lupa ikuti serial selanjutnya!
18 Definisi Tipe Data `User` di Skema GraphQL
Artikel Terhangat
22 Membuat Sub-Schema Berdasarkan Modul
07 Jul 2025
44 Reuse Message dengan `import`
07 Jul 2025
43 Memanfaatkan `oneof` di Protobuf
07 Jul 2025
42 Menggunakan Enum di Protobuf
07 Jul 2025
19 Implementasi Resolver untuk Query `users`
07 Jul 2025
41 Memahami Nested Messages
07 Jul 2025

22 Membuat Sub-Schema Berdasarkan Modul

44 Reuse Message dengan `import`

43 Memanfaatkan `oneof` di Protobuf

42 Menggunakan Enum di Protobuf

19 Implementasi Resolver untuk Query `users`
