Proxy Pattern #
Dalam software engineering, tidak semua objek sebaiknya diakses secara langsung oleh client. Ada kondisi di mana kita perlu mengontrol akses, menambahkan perilaku tambahan, atau mengoptimalkan resource tanpa mengubah implementasi asli objek tersebut. Di sinilah Proxy Pattern berperan.
Proxy Pattern adalah salah satu structural design pattern yang berfungsi sebagai perantara (stand-in) untuk objek lain. Proxy memiliki interface yang sama dengan objek asli, sehingga client tidak sadar bahwa ia sebenarnya berinteraksi dengan proxy, bukan objek sebenarnya.
Apa itu Proxy Pattern? #
Proxy Pattern adalah pattern yang menyediakan objek pengganti (proxy) untuk mengontrol akses ke objek asli (real subject).
Proxy bertindak sebagai lapisan tambahan antara client dan objek target. Semua request dari client akan melewati proxy terlebih dahulu sebelum diteruskan (atau tidak diteruskan) ke objek asli.
Secara konseptual:
Client → Proxy → Real Object
Client hanya mengenal interface, bukan implementasi konkret.
Tujuan dari Pattern #
Proxy Pattern digunakan untuk:
Mengontrol akses ke objek
- Authorization, authentication, rate limiting
Menunda pembuatan objek berat (lazy initialization)
- Menghemat memory dan resource
Menambahkan perilaku tambahan tanpa mengubah kode asli
- Logging, caching, monitoring
Mengelola objek yang berada di remote system
- RPC, API client, microservices
Melindungi objek dari akses langsung
- Validasi, sanitasi input
Kapan Cocok Digunakan? #
Gunakan Proxy Pattern jika:
- Objek asli mahal dibuat (heavy initialization)
- Anda ingin menambahkan logging, metrics, atau tracing
- Akses ke objek perlu dibatasi atau divalidasi
- Anda ingin memisahkan concern non-bisnis dari core logic
- Client tidak perlu tahu apakah objek tersebut lokal atau remote
Tidak cocok digunakan jika:
- Logic tambahan sangat sederhana dan tidak reusable
- Menambah proxy justru membuat kode terlalu kompleks
Struktur Pattern #
Komponen utama:
Subject (Interface)
- Kontrak yang digunakan client
RealSubject
- Implementasi asli
Proxy
- Mengimplementasikan interface yang sama
- Menyimpan reference ke RealSubject
Jenis-Jenis Proxy Pattern #
Proxy Pattern sering dibagi menjadi beberapa varian:
Virtual Proxy #
Menunda pembuatan objek sampai benar-benar dibutuhkan.
Protection Proxy #
Mengontrol siapa yang boleh mengakses objek.
Remote Proxy #
Mewakili objek yang berada di sistem lain (network).
Caching Proxy #
Menyimpan hasil request agar tidak selalu memanggil objek asli.
Logging / Monitoring Proxy #
Menambahkan observability tanpa mengubah core logic.
Contoh Implementasi (Golang) #
Studi Kasus #
Misalnya kita memiliki service untuk mengambil data user dari database atau API eksternal. Kita ingin menambahkan logging dan caching tanpa mengubah service aslinya.
Subject Interface #
type UserService interface {
GetUser(id int) (string, error)
}
Real Subject #
type RealUserService struct {}
func (s *RealUserService) GetUser(id int) (string, error) {
// simulasi operasi mahal
time.Sleep(2 * time.Second)
return fmt.Sprintf("User-%d", id), nil
}
Proxy Implementation #
type UserServiceProxy struct {
realService UserService
cache map[int]string
}
func NewUserServiceProxy(real UserService) *UserServiceProxy {
return &UserServiceProxy{
realService: real,
cache: make(map[int]string),
}
}
func (p *UserServiceProxy) GetUser(id int) (string, error) {
// logging
log.Printf("GetUser called with id=%d", id)
// caching
if user, ok := p.cache[id]; ok {
log.Println("Return user from cache")
return user, nil
}
user, err := p.realService.GetUser(id)
if err != nil {
return "", err
}
p.cache[id] = user
return user, nil
}
Penggunaan di Client #
func main() {
realService := &RealUserService{}
proxy := NewUserServiceProxy(realService)
user1, _ := proxy.GetUser(1)
fmt.Println(user1)
user2, _ := proxy.GetUser(1)
fmt.Println(user2)
}
Output:
- Pemanggilan pertama: lambat (real service)
- Pemanggilan kedua: cepat (cache)
Client tidak tahu apakah ia menggunakan proxy atau real object.
Proxy Pattern vs Decorator Pattern #
Walaupun mirip, keduanya memiliki perbedaan konsep:
| Proxy | Decorator |
|---|---|
| Fokus pada kontrol akses | Fokus pada penambahan behavior |
| Proxy sering dibuat oleh system | Decorator dipilih oleh client |
| Umumnya satu proxy | Bisa banyak decorator bertingkat |
Di dunia nyata, implementasinya sering terlihat mirip, tetapi niat desainnya berbeda.
Kelebihan dan Kekurangan #
Kelebihan #
- Open/Closed Principle
- Separation of concerns
- Mudah menambahkan cross-cutting concern
Kekurangan #
- Menambah kompleksitas
- Lebih banyak layer untuk di-debug
Praktik Nyata di Golang #
Proxy Pattern sering muncul secara implisit dalam:
- HTTP middleware
- gRPC interceptor
- Repository wrapper
- API client dengan retry & circuit breaker
Banyak developer Golang menerapkan proxy tanpa menyebutnya sebagai “Proxy Pattern” secara eksplisit.
Best Practices #
Gunakan interface sebagai kontrak
- Hindari ketergantungan ke concrete struct
Jaga proxy tetap tipis
- Jangan masukkan business logic utama ke proxy
Pisahkan concern non-bisnis
- Logging, caching, auth, metrics
Perhatikan concurrency (di Golang)
- Gunakan mutex jika proxy menyimpan state (cache)
var mu sync.Mutex
mu.Lock()
defer mu.Unlock()
Jangan over-engineering
- Jika middleware sudah cukup, proxy mungkin tidak dibutuhkan
Penutup #
Proxy Pattern adalah solusi elegan untuk mengontrol akses dan menambahkan perilaku tambahan ke sebuah objek tanpa mengubah kode aslinya. Dalam ekosistem Golang, pattern ini sangat relevan untuk logging, caching, observability, dan integrasi sistem eksternal.
Gunakan Proxy Pattern ketika Anda membutuhkan lapisan kontrol tambahan, tetapi tetap jaga desain agar sederhana dan tidak berlebihan.