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:

  1. Mengontrol akses ke objek

    • Authorization, authentication, rate limiting
  2. Menunda pembuatan objek berat (lazy initialization)

    • Menghemat memory dan resource
  3. Menambahkan perilaku tambahan tanpa mengubah kode asli

    • Logging, caching, monitoring
  4. Mengelola objek yang berada di remote system

    • RPC, API client, microservices
  5. 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:

  1. Subject (Interface)

    • Kontrak yang digunakan client
  2. RealSubject

    • Implementasi asli
  3. 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:

ProxyDecorator
Fokus pada kontrol aksesFokus pada penambahan behavior
Proxy sering dibuat oleh systemDecorator dipilih oleh client
Umumnya satu proxyBisa 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 #

  1. Gunakan interface sebagai kontrak

    • Hindari ketergantungan ke concrete struct
  2. Jaga proxy tetap tipis

    • Jangan masukkan business logic utama ke proxy
  3. Pisahkan concern non-bisnis

    • Logging, caching, auth, metrics
  4. Perhatikan concurrency (di Golang)

    • Gunakan mutex jika proxy menyimpan state (cache)
var mu sync.Mutex

mu.Lock()
defer mu.Unlock()
  1. 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.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact