Using service facades in a Symfony application — part 1

Thierry Feuzeu
5 min readMar 27, 2025

--

Symfony developers do not like facades!

I learnt it the hard way as I was trying to promote an open source package I wrote a while ago, which implements service facades for the Symfony framework: https://github.com/lagdo/symfony-facades.
What??!! Facades in a Symfony application??!! I was answered many times, as if I was committing a sacrilege.

Yes Sir, you heard me correctly. Facades in Symfony.
I needed to understand why some Symfony developers hate facades so much. So I read a many articles on the subject, read the comments people wrote, and also read a few discussions on Reddit and StackOverflow.

After that, it appears to me that they’ve been a couple of misunderstandings about facades, or service facades, as I like to call them. And I think it’s now the time to add my two cents to the subject.

This is the first of a two parts article where I’ll discuss what facades are, what they are not, and when it may be useful to use them in a Symfony application.

Edit: Read the part 2 here.

What facades are not

The main reasons mentioned by Symfony developers against service facades are their static method syntax, and the claim that they introduce a global state in the application. Let’s start by discussing these two statements.

Service facades are not static classes

A facade is generally called with a static method syntax, like in the following example.

class A
{
B::f();
}

Because of the similar syntax, many developers have associated the drawbacks of static methods to service facades.

When a real static method is called with the above syntax, the calling class is strongly coupled to the called method, because the call can’t be dynamically changed. It is for instance impossible to replace the call with a mock in a testing environment, or with a call to another method that would provide extended or custom feature depending on certain parameters.
The “A” class will always call the same and only implementation of the “B:f()” method.

But if “B” is a facade, the situation is very different. The “B” class actually doesn’t have any static method “f()”, which instead is a public and non static method in a “C” class registered in the service container. The call to “B” can be mocked when testing the “A” class, and can be replaced to provide extended or custom feature, for example by a “D” class that extends “C”, just like it would be the case if the “C” class was injected in the “A” constructor.

The service facades then provide the same level of flexibility and customization as the dependency injection. Apart from a reference to the corresponding service, there is no user variable or method in a service facade, and hence no state, be it global or anything else, involved.

Service facades do not add a dependency on the service container

Another argument against service facades is that they introduce a dependency on the service container. However, even with dependency injection, the classes already depend on the service container. In fact, without the service container to bound them together, each class taken individually is somehow just a useless piece of code. They can’t from example be migrated from a framework to another without also migrating the service container code.

Service facades just make more noticeable a dependency that already exists.

I also found some people arguing that dependency injection is better than service facades because all the dependencies of a class can be learnt just by looking at its constructor. I may be wrong, but I do not consider an issue something that can be solved with a few comment lines.

Service facades are not portable

Last but not the least, another reason while developers (not only Symfony this time) would recommend against using facades is that the resulting classes would be bound to a given framework. That’s actually true. But the reason why service facades are not portable is not related to the facades themselves, but rather to the fact that they are not “standardized”.

The reason why a given class can be ported from a framework to another is not only because the class is framework agnostic, but also because the class implements well-defined interfaces that allow other classes to know how to call its methods. The same applies to service facades. Without common rules or even a package à la PSR to define how they are used, they will always be bound to the framework the package providing their features is built for.

What service facades are

It might be quite surprising to say, especially considering the reputation they are facing now, but service facades are actually just a different kind of dependency injection.

Let’s take a minute and think about it for a while.

When a class uses a service facade, the only thing it initially holds is a reference to another class, which is this case is the facade class. For ordinary dependency injection, that initial reference is a member attribute without any value.

At a certain point, the service container creates an instance of an other class, which it then passes to, or injects into the former. While for ordinary dependency injection, that injection happens when the class is instantiated, for a service facade, it happens at each call to a facade, which is automatically forwarded to a service that is fetched from the service container.

So the service mapped by a facade is also injected as a dependency, but with another notable difference, in addition to the fact that they are called at different moments: the class takes the initiative of calling the service container when it needs a dependency, instead of letting the service container call one of its methods.

Dependency injection and inversion of control

If service facades are a different implementation of the dependency injection pattern, then how would the “ordinary” dependency injection implementation be named? The answer is quite simple, and rather well-known: it’s the inversion of control pattern.

According to Wikipedia, inversion of control (IoC) is a design principle in which custom-written portions of a computer program receive the flow of control from an external source (e.g. a framework).

As a matter of fact, the service container is also called the IoC container in many places. What is currently called dependency injection is actually an IoC based implementation of the dependency injection pattern. That means a kind of dependency injection where the service container is responsible of passing the dependencies through a selected set of user defined methods.

This usage of IoC also confers its main limitation to this dependency injection implementation: a dependency can only be injected in user functions that are called by the framework. This includes, among a few others, the class constructors, some setters in user defined services, or the methods attached to a route in a controller.

Conclusion

In conclusion, service facades are a different kind of dependency injection, without the inversion of control (IoC). Contrary to popular belief, they are not static classes, and they do not add a global state or an additional dependency on the service container in the application. Of course, a class depending on a service facade will bound to a given framework, and thus not portable across frameworks.

In the second part of the article, I’ll present some examples when using a service facade in a Symfony application may be the best, or even the only developer option.

Edit: Read the part 2 here.

--

--

No responses yet