110 Custom Model Mapping: Menghubungkan Tipe ke Struct Sendiri
Saat membangun aplikasi backend atau aplikasi enterprise berskala besar, sering kali kita dihadapkan pada kebutuhan untuk melakukan data mapping. Tidak jarang pula, model data dari sumber eksternal (seperti response dari service lain, database, atau API pihak ketiga) tidak langsung sesuai dengan model atau struct yang kita gunakan dalam aplikasi. Custom model mapping menjadi solusi elegan untuk kebutuhan ini: cara kita menyambungkan tipe data “asing” ke dalam struct buatan sendiri dengan aturan main kita sendiri.
Pada artikel ini, saya akan membahas secara mendalam teknik “custom model mapping”, menyorot konsep general hingga ke praktik implementasinya dengan contoh kode konkret, simulasi kasus, dan penggunaan flowchart untuk memetakan logika mapping yang baik. Fokus utamanya pada bahasa Go (Golang), tapi pola-nya sebenarnya sangat universal untuk hampir semua bahasa bertipe statis dan statically typed OOP.
Mengapa Butuh Custom Model Mapping?
Skenario berikut pasti tak asing:
- API eksternal punya field yang aneh atau tak relevan
- Nama field berbeda antara response eksternal dan domain internal
- Field nullable di API, tapi di domain harus non-null
- Logical transformation: status code “1/0” jadi “active/inactive”
Jika kita langsung memroses data di seluruh lapisan aplikasi, codebase akan jadi “fragile”—rapuh, rawan bug, dan sulit maintenance. Praktik baiknya: mapping data sedini mungkin ke model internal yang dirancang sesuai kebutuhan, lalu seluruh logic selanjutnya jalan dengan model internal ini.
Tabel Perbandingan Model Eksternal vs Model Internal
Mari ambil contoh konkret. Anggap kita punya service eksternal dengan response sbb (dalam JSON):
Field Eksternal | Tipe | Sample Value | Keterangan |
---|---|---|---|
user_id | int | 2024 | Sama dengan id user |
name | str | “Andi Online” | Nama user |
stat | int | 1 | 1 = aktif, 0 = non aktif, bisa null |
is_admin | bool | false | Benar/salah, opsional |
Sedangkan struct internal kita dirancang sbb:
Field Internal | Tipe | Keterangan |
---|---|---|
ID | int | User ID |
FullName | string | Nama lengkap user |
Status | string | “active” atau “inactive” |
IsSuperAdmin | bool | True hanya jika admin |
Model Struct: Eksternal vs Internal
// Response dari API eksternal
type ExternalUser struct {
UserID int `json:"user_id"`
Name string `json:"name"`
Stat *int `json:"stat"` // Bisa null
IsAdmin *bool `json:"is_admin"` // Bisa null
}
// Struct internal aplikasi kita
type User struct {
ID int
FullName string
Status string // "active" atau "inactive"
IsSuperAdmin bool
}
Flow Mapping: Diagram Alur Data
Mari visualisasikan proses mapping tipe dalam bentuk flowchart menggunakan mermaid:
flowchart TD A[Ambil data dari API eksternal] --> B{Parsing JSON ke ExternalUser} B --> C{Transform} C -->|Stat==1| D[Status="active"] C -->|Stat==0|null| E[Status="inactive"] C --> F{IsAdmin==true} F -->|ya| G[IsSuperAdmin=true] F -->|tidak| H[IsSuperAdmin=false] D & G --> I[Bentuk struct User] E & H --> I I --> J[Simpan/Proses User di aplikasi]
Alur ini memastikan data “kotor” eksternal, langsung bersih ketika masuk ke model internal aplikasi.
Custom Mapping: Cara Implementasi
Kita ingin membuat fungsi MapExternalUserToUser
yang dapat dikustom sesuai aturan bisnis:
func MapExternalUserToUser(ext ExternalUser) User {
status := "inactive"
if ext.Stat != nil && *ext.Stat == 1 {
status = "active"
}
isSuperAdmin := false
if ext.IsAdmin != nil && *ext.IsAdmin {
isSuperAdmin = true
}
return User{
ID: ext.UserID,
FullName: ext.Name,
Status: status,
IsSuperAdmin: isSuperAdmin,
}
}
Penjelasan singkat:
- Mengamankan nilai
Stat
yang nullable (nil
) supaya aman (default “inactive”) - Translasi field nama berbeda (misal,
user_id
keID
,name
keFullName
) - Default logic
IsSuperAdmin
jika nil
Simulasi Penggunaan: Kode & Testing
Bayangkan kita mendapat data dari endpoint eksternal seperti ini:
{
"user_id": 2024,
"name": "Andi Online",
"stat": 1,
"is_admin": null
}
Contoh kode mengkonsumsi dan mapping:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// Simulasi response JSON eksternal
data := []byte(`{"user_id":2024, "name":"Andi Online", "stat":1, "is_admin":null}`)
var extUser ExternalUser
json.Unmarshal(data, &extUser)
mappedUser := MapExternalUserToUser(extUser)
fmt.Printf("%+v\n", mappedUser)
// Output: {ID:2024 FullName:Andi Online Status:active IsSuperAdmin:false}
}
Unit test implementasi mapping:
func TestMapExternalUserToUser(t *testing.T) {
statVal := 1
adminVal := false
ext := ExternalUser{
UserID: 1,
Name: "Ujang",
Stat: &statVal,
IsAdmin: &adminVal,
}
u := MapExternalUserToUser(ext)
if u.Status != "active" || u.IsSuperAdmin {
t.Errorf("Mapping gagal: %+v", u)
}
}
Patterns & Best Practice
- Jangan mapping di seluruh kode. Sediakan satu lapisan mapping; lewati layer service/business hanya struct internal.
- Selalu cover kasus null/unexpected. Contoh di atas gunakan pointer dan default value.
- Gunakan unit test untuk handling edge-case pada mapping.
- Jika kemungkinan berubah-ubah, split fungsi mapping per konteks, hindari god function.
Scaling: Mapping Banyak Data (Bulk Mapping)
Jika kita sering menerima dalam bentuk slice/array, gunakan helper:
func MapExternalUsers(exts []ExternalUser) []User {
var users []User
for _, e := range exts {
users = append(users, MapExternalUserToUser(e))
}
return users
}
Mapping Library: Pakai Atau Tidak?
Ada library mapping otomatis (untuk Go misal: https://github.com/mitchellh/mapstructure, untuk Java: MapStruct). Tapi hati-hati: mapping kustom sering melibatkan logika domain, jadi mapping manual tetap solusi utama jika aturannya kompleks.
Studi Kasus: Error Handling Saat Mapping
Cek kondisi “bad data” dari eksternal, return error jika tidak map-able.
func MapExternalUserToUserV2(ext ExternalUser) (User, error) {
if ext.UserID == 0 {
return User{}, fmt.Errorf("user_id harus ada")
}
// lanjut mapping seperti biasa...
...
}
Kesimpulan
Mapping custom model, dari tipe eksternal ke struct sendiri, adalah pola fundamental dalam software engineering. Model decoupling seperti ini membebaskan aplikasi dari “beban warisan” data format eksternal, menjaga codebase tetap rapih, logis, dan mudah di-maintain. Sediakan fungsi mapping sedini mungkin, investasikan waktu di testing, dan atur edge-case dengan teliti. Dalam jangka panjang, model seperti ini pasti hemat biaya maintenance dibanding tempel data seenaknya di seluruh kode.
Sudahkah Anda punya “Custom Model Mapper” dalam aplikasi yang sedang Anda kerjakan hari ini?
109 Menghubungkan Resolver dengan Model Database
Artikel Terhangat
108 Menangani Resolver Otomatis dan Manual
10 Oct 2025
106 Menulis Skema `.graphqls` untuk gqlgen
10 Oct 2025
104 Struktur File gqlgen dan Cara Kerjanya
10 Oct 2025

108 Menangani Resolver Otomatis dan Manual

106 Menulis Skema `.graphqls` untuk gqlgen
