Lesson 13 / 25

Proxy

Caching, lazy loading and access control.

A stand-in with the same interface

Intent: provide a surrogate that controls access to another object while exposing the same interface. Common kinds: a caching proxy returns stored results instead of calling a slow service; a virtual proxy delays creating an expensive object until it is first used (lazy loading); a protection proxy checks permissions before forwarding; a remote proxy makes a network service look like a local object (RPC client stubs). Structurally a proxy resembles a decorator; the difference is intent: a proxy controls access to the subject, a decorator adds behaviour. JavaScript also has a built-in Proxy object that intercepts property access, used by some reactivity systems and ORMs. Avoid proxies that hide expensive network calls behind innocent-looking property reads.

Stand-ins, trees and shared parts

More ways to compose objects: control access, model hierarchies, and separate or share state.

Three ideas: proxies, composites, and a Bridge and Flyweight overview.
Figure 5.1 — Proxy, composite tree and shared flyweights.

A caching and protection proxy

Same interface as the real product catalogue.

interface ProductCatalog {
  getProduct(id: string): Promise<Product>;
}

class CachingCatalogProxy implements ProductCatalog {
  private cache = new Map<string, { value: Product; expires: number }>();
  constructor(private real: ProductCatalog, private ttlMs = 60_000) {}

  async getProduct(id: string) {
    const hit = this.cache.get(id);
    if (hit && hit.expires > Date.now()) return hit.value;
    const value = await this.real.getProduct(id);
    this.cache.set(id, { value, expires: Date.now() + this.ttlMs });
    return value;
  }
}

class AdminOnlyCatalogProxy implements ProductCatalog {
  constructor(private real: ProductCatalog, private user: User) {}
  getProduct(id: string) {
    if (!this.user.roles.includes('admin')) throw new ForbiddenError();
    return this.real.getProduct(id);
  }
}

const catalog: ProductCatalog = new CachingCatalogProxy(new HttpProductCatalog(baseUrl));

Plan cache invalidation

A caching proxy needs a clear answer to "when does stale data become wrong?": a time-to-live, explicit invalidation on writes, or both.

Quick check: Which is an example of a virtual proxy?

  • Converting one interface into another
  • Delaying loading a large image or object until it is first used
  • Notifying subscribers of changes
  • Copying an existing object
Answer

Delaying loading a large image or object until it is first used — Virtual proxies support lazy loading.