Structural Surrogate

Proxy

Proxy stands in for another object behind an identical interface, letting it intercept every call to add checks, caching, lazy creation, or logging without the real object or its callers knowing.

Complexity
Popularity

Problem

A reporting service wraps a slow, network-bound call to a remote analytics API. Every screen that displays a report re-triggers that call, even when the underlying data hasn't changed in the last five minutes - burning bandwidth and making the UI feel sluggish.

The obvious fix is to add caching, but the service class is generated from an API spec (or shared across teams), so editing it directly means the caching logic gets tangled with unrelated code, or lost on the next regeneration. Meanwhile the service object itself is expensive enough to construct that creating it eagerly at startup - before anyone has even opened a report - wastes resources on every run.

Solution

Introduce a Proxy class that implements the exact same interface as the service and gets substituted for it everywhere the service was expected. Every call to the proxy first passes through its own logic - check the cache, verify permissions, construct the real service on first use - before optionally delegating to the wrapped object.

Because the interface is identical, nothing on the calling side changes: code that used to hold a service reference now holds a proxy reference and is none the wiser. The original service class stays untouched, so its logic and the cross-cutting behavior evolve independently.

When to Use

  • A service object is expensive to create, and most execution paths through the application never actually end up needing it.
  • Only certain callers should be allowed to invoke a service, and that check needs to live outside the service's own logic.
  • Repeated calls with the same arguments return the same result for a meaningful window of time, making caching worthwhile without touching the service's implementation.

Real-World Examples

  • **Nginx / HAProxy reverse proxies** - sit in front of application servers, terminating TLS, rate-limiting, and serving cached responses before a request ever reaches the backend.
  • **The built-in JavaScript `Proxy` object** - wraps a target object and lets you intercept `get`, `set`, and function-call traps, which is how libraries like Vue 3 implement reactive data.
  • **ORM lazy-loaded associations (Hibernate, SQLAlchemy, Entity Framework)** - a related entity is represented by a placeholder proxy object, and the actual SQL query only fires the first time a field on it is accessed.