Urgent.News

the world's headlines, one feed

Tech

Proxy Pattern: control access without changing the interface

Il problema: oggetti pesanti che non servono subito Hai un modello Article con una relazione comments() . La pagina del blog elenca 20 articoli con titolo, data e autore. Nessuno ha ancora cliccato su un articolo per vederne i commenti, eppure il codice carica tutte le relazioni in anticipo, eseguendo 21 query (una per gli articoli e una per i commenti di ciascuno). E il classico N+1 problem , ma…

Translated from Italian Read in Italian

The problem: heavy objects that are not needed immediately

You have an Article model with a comments() relationship. The blog page lists 20 articles with title, date, and author. No one has clicked on an article to view its comments yet, but the code loads all relationships in advance, executing 21 queries (one for the articles and one for the comments of each). This is the classic N+1 problem, but the root is deeper: the problem is that the Article object directly exposes comments, and whoever uses it has no way to decide when to load them.

The Proxy Pattern solves this problem by interposing a surrogate object (the proxy) between the client and the real object. The proxy implements the same interface as the real object but controls when and how the real object is created or accessed. The client does not know it is talking to a proxy — for it, it is the same object as always.

What is the Proxy Pattern: definition and variants

The Gang of Four defines the Proxy as a structural pattern that "provides a surrogate or placeholder for another object to control access to it." The structure is similar to the Decorator — both wrap an object with the same interface — but the intent is different: the Decorator adds behavior, the Proxy controls access.

There are several variants of the Proxy, each with a specific purpose:

* Virtual Proxy (Lazy Loading): delays the creation of the real object until the first use. Useful for heavy objects that may never be needed.

* Protection Proxy: checks permissions before delegating the call. The real object knows nothing about authorization — the proxy handles it externally.

* Cache Proxy: stores the result of the real object and returns it directly to subsequent calls, avoiding expensive recalculations.

* Remote Proxy: represents an object that lives on another server or process. The client calls local methods, and the proxy translates them into network calls.

* Logging Proxy: logs every call to the real object for debugging or auditing without the business code containing logging logic.

Theoretical example: lazy loading of ORM relationships

Consider an ORM that loads an Article from the database. The article has a property $comments that should contain an array of Comment objects. With eager loading, all comments are loaded immediately. With the Proxy Pattern, the property initially contains a CommentCollectionProxy. The CommentCollectionProxy implements the same interface as a collection (e.g., Countable, IteratorAggregate, ArrayAccess).

Internally, it maintains a flag $loaded = false and a reference to the query needed to load the data. When someone calls count() or iterates over the collection, the proxy executes the query, stores the result, and delegates the call to the real collection. On subsequent calls, the data is already in memory.

The advantage: N+1 becomes 1+0

If the blog page shows only the title and date, no proxy is ever resolved: zero queries for comments. If the user opens a specific article, the proxy for that article resolves comments with a single query. The pattern transforms the N+1 problem into a "1 + only what is needed" without changing the controller code by one line.

Theoretical example: Protection Proxy for APIs

Imagine a ReportService with a method generateFinancialReport(). This method can only be called by users with the role "admin" or "finance". Instead of adding permission checks inside the service (violating SRP), you create an AuthorizedReportServiceProxy:

* The proxy receives the real ReportService and an AuthorizationChecker in the constructor.

* Before delegating generateFinancialReport(), it checks that the current user has the permission report.financial.generate.

* If the permission is missing, it throws a ForbiddenException without ever calling the real service.

The ReportService remains clean: it generates reports, period. It knows nothing about permissions.

Theoretical example: Cache Proxy for external API calls

A WeatherService calls an external API to get the weather forecast. Each call costs 200ms of latency. A CachedWeatherProxy wraps the service: on the first call, it delegates to the real service and saves the result in cache with a TTL of 30 minutes. On subsequent calls, it returns the data from the cache in less than 1ms. The controller does not know if it is receiving fresh or cached data — and it does not need to know.

Proxy vs Decorator: the subtle difference

Proxy and Decorator have the same structure: a wrapper with the same interface as the wrapped object. The difference is in the intent:

* Decorator: adds new behavior. The focus is on extending functionality.

* Proxy: controls access to the existing object. The focus is on when, how, and if the object is used.

In practice: a LoggingDecorator adds logging as an extra functionality. A LazyProxy controls when the object is created. A ProtectionProxy controls whether the object is accessed.

The distinction is conceptual, not structural — and it is essential for communicating the intent of the code to those who read it.

When to use the Proxy

* Lazy Loading: when creating the object is expensive and may not be needed (ORM relationships, connections, heavy files)

* Access Control: when you want to separate authorization logic from business logic

* Caching: when the result is expensive to calculate but stable over time

* Remote Access: when the real object lives on another server and you want to hide network complexity

Do not use the Proxy if:

* the object is light and always used: the proxy adds indirection without benefit

* transparency becomes a problem: sometimes it is better to make it explicit that an operation is lazy or cached

The Proxy Pattern is one of the most invisible patterns — when it works well, no one knows it is there. And this is its strength: the business code remains clean, and access control, caching, or loading is handled in a single point, testable and modifiable independently.

Translated by urgent.news from Dev.to's report. Machine-written; read the original for the full account.

Read the original at dev.to →

More in Tech