tutorial

41 Memahami Nested Messages

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 dalam AddressBook.
  • Field people akan mengandung daftar objek Person.

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

SkenarioNested MessageTop-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 generate addressbook.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

KelebihanKekurangan
Namespace lokal, mengurangi tabrakanTidak dapat di-reuse lintas file/message
Struktur data lebih jelas dan rapiSulit di-maintain jika kedalaman bersarang
Tipe hanya terkait parentDependencies 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!

comments powered by Disqus

Topik Terhangat

programming
221
tutorial
88
tips-and-trick
43
jaringan
28
hardware
11
linux
4
kubernetes
1