[Disponibile anche in italiano]
Recently, I worked on a project that requires dealing with the same problem using services provided by two different vendors. The business logic is shared, but some implementation details depend on the formats used and the specifications of each vendor.
It is a perfect use case for applying the SOLID principles and Clean Architecture. Specifically, we achieved this by using Symfony and its implementation of Dependency Injection.
Some interfaces define behaviors that require different implementations for the two vendors. Each interface contains methods that operate on the same concept or work within the same scope. The goal is always to maintain high cohesion within an interface and low coupling between interfaces.
In addition, all interfaces contain a static method that returns the string identifying the supported vendor.
Implementation with Symfony
The services that implement the mentioned interfaces, specific to a particular vendor, are tagged using the
_instanceof key in the configuration file.
The services that implement the business logic have one or more Service Locators among their dependencies. Service Locator is a construct of Dependency Injection in Symfony that allows access to a subset of services contained in the service container, in this case, those with a specific tag.
We use it to retrieve the implementation for a specific vendor, using the corresponding key associated with it, among all the implementations written for a particular problem, formalized in one of the interfaces.
SOLID Principles applied
Let’s see which of the SOLID principles we have applied with this solution:
- Open-closed principle: if there is a requirement to support a new vendor, it should only involve adding the implementations of the interfaces for the new vendor, without the need to modify the existing business logic
- Interface segregation principle: by breaking down the behaviors into different interfaces, each service implementing the business logic utilizes only the interfaces and their respective Service Locators that it actually needs
- Dependency inversion principle: our business logic depends on abstractions, which are the interfaces, and not explicitly on the implementations for the various vendors
In this short article, we have seen how it is possible to use Dependency Injection constructs in Symfony, particularly tags and Service Locators, to easily apply some of the SOLID principles and make the application more elegant and easier to maintain.