Angular: Bad Practices/Patterns: The Proxy Service Pattern

Ryan Mackey-Paulsen
3 min readJan 24, 2020

--

Hide all of your problems with a Proxy Service!

I have seen this one in a few places and have seen a few blogs suggesting this as well as had it suggested to me directly. If you have not heard of this pattern, let me explain the reasoning for why it is used.

Say you have a component that has a lot of functionality. Let’s say it is an audio player. So you would start with something like this:

@Component({
selector: 'audio-player',
templateUrl: './audio-player.component.html',
styleUrls: ['./audio-player.component.less'],
encapsulation: ViewEncapsulation.None
})
export class AudioPlayerComponent {}

Now, in order for this player to work you will need to load the audio, ability to skip tracks, play, pause, and stop. You could even load some metadata about the song from another service. Maybe even showing upcoming titles from a playlist would be nice.

So now your constructor looks something like this:

constructor(private audioLoader: AudioLoaderService,
private audioPlayerService: AudioPlayerService,
private audioMetadata: AudioMetadataService,
private playlistService: PlaylistService) {}

Now this is just the beginning and the dependencies will continue to grow as more features are added. This can start to get out of hand and become harder to test as the dependency list grows. Ahh, what are you going to do?

The Proxy Pattern

The proxy pattern is simply to create a new service that combines all of those dependencies and only exposes the methods from those dependencies that the component needs by supplying methods that simply pass through to the methods of those services. So the above constructor becomes:

constructor(private proxyService: ProxyAudioPlayerService) {}

And the Proxy service might look something like this:

class ProxyAudioPlayerService {
constructor(/*your dependencies*/){}

loadAudio(id: string){
return this.audioLoader.load(id);
}

play() {
return this.audioPlayerService.play();
}
pause() {
return this.audioPlayerService.pause();
}
... and so on
}

Now this looks like a great way to clean up your component! So what’s wrong with this?

The Problem

If you see this pattern there is one thing that should be screaming at you, and that is that the component needs to be broken up. A parent component that holds all of the functionality is not so bad when the functionality is small, but as it grows, you need to focus on smaller components that do more specific, isolated tasks. Using the Proxy Pattern will only mask the true issue at hand, and that is that you have one component doing all of the work.

In the example of the audio player, you can still have a parent audio player, but it can then have a playlist component that handles the playlists. There can be a controls component that handles playing, pausing, and skipping. There can then be a single player service that the parent player and controls share. That way if you load a new song, the same service is responsible for stopping the current audio. The parent component can still be the delegator of work, but it doesn’t need to have access to all of the underlying services in its dependencies, but instead can listen for events on the children.

Whatever the use case is for you, if you see this pattern, you should consider breaking up the components before running down this path. The arguments for the benefits are also easily debunked. This can’t help with testing as now you also have to test the proxy service and you still run into the issue of testing with a large set of dependencies. You also have created a new level of abstraction in order to keep you constructor clean, but all of your code complexity remains. This just simply moves the problem out of the component and into a service.

So Don’t Do It

This is most likely an example from a developer that is not yet comfortable with the idea of building applications with components. The idea of building components from other components is one of those foreign concepts that, once you understand, can help with your entire application architecture. You can start to see reusability, component interactions, and isolated functionality. You could even have children components be actual web components or from another library, and the parent could wrap the other components and delegate events and interactions. And that is a much better pattern than simply creating another level of abstraction.

--

--

Ryan Mackey-Paulsen

Web App Developer, Web Component Enthusiast, Musician, Human