service and context - building a context-dependent api
TRANSCRIPT
![Page 1: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/1.jpg)
Service and contextBuilding a context-dependent API
By Michał Kurzeja
![Page 2: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/2.jpg)
A particularly challenging project we developed included building a brand new API featuring methods to export data to partner services. That meant:
whatever was released could no longer be modified to maintain the integrity of the system a need to develop an easy way to add new features to the API doing it all without copying tons of code and in line with the logic of the applications
The answer I eventually came up with was...
![Page 3: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/3.jpg)
...to make the services dependent on the context
Any number- or string-based value could serve as such:
API version session parameter user setting
This approach produces code as follows:
![Page 4: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/4.jpg)
With an approach like this:
an object is produced for each class defined for a given context there is no limit to the number of contexts we can create for each service we can define dependencies for each context
![Page 5: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/5.jpg)
With service tagging and container compiling, we can use tags to make every service we wish context-dependent. For example:
![Page 6: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/6.jpg)
Explanation:
context.sensitive is the tag name it accepts two arguments: context and alias alias groups services that perform the same task we pick a service based on the value of the context parameter (e.g. for the processor service retrieved in the a context, the ProcessorA object is created, and for the b context – ProcessorB) the processor.universal alias returns ProcessorAB for both a and b con- text
![Page 7: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/7.jpg)
We need a class that delivers each service when needed. Let’s call it ContextAwareContainer and start with defining its interface:
![Page 8: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/8.jpg)
In the next few slides, we will go through an example of a class that implements the interface
![Page 9: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/9.jpg)
Explanation:
ContextAwareContainer is a service that holds a reference to the con- tainer the inner array $services contains the IDs of services we reference through context and alias the set(...) method shown in the pre- vious slide builds the array the get(...) method shown in the pre- vious slide returns a service based on context and alias passed as arguments
![Page 10: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/10.jpg)
Explanation:
the initializable interface prevents more services from being registe- red right after the initialization
![Page 11: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/11.jpg)
The definition of the class as a service:
![Page 12: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/12.jpg)
The last step is to create an object that implements the CompilerPassInterface interface. It finds the services we tagged and saves their data in the ContextAwareContainer.
![Page 13: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/13.jpg)
Explanation:
the findTaggedServiceIds(…) method accepts an array the indices of the array are the IDs of services that have the context. sensitive tag the values of the array are the attri- butes of those tags all the data is used as arguments of the set(...) method
![Page 14: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/14.jpg)
At this point we can already use the ContextAwareContainer to select services based on context.
Let’s further this concept by considering another scenario:
![Page 15: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/15.jpg)
What if we had no choice but to make our service depen-dent on another service and the class of the dependency
changes with the context? For example:
To achieve this, we have to find a way to pass the alias of the dependency to the definition of the service. Let’s see
an example.
Service
Context::A Context::B
ADependency
BDependency
![Page 16: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/16.jpg)
Explanation:
the properties attribute is a multi- dimensional array that accepts any type of value the dynamic_arguments is an array. Its keys are contexts and its valu- es are an array of aliases the new setup is as follows: when we retrieve the processor.depen- dant service with the a context, it passes a reference to a servi- ce with the processor alias and the a context (that is an object of the ProcessorA class) as its de- pendency. However, if the con- text equals b, a service of the ProcessorB class is injected into an object of the ProcessorABDependant class.
![Page 17: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/17.jpg)
We now have to modify the ContextAwareInterface so that it stores the information on the dynamic dependencies.
![Page 18: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/18.jpg)
Explanation:
we expand the set(…) function so that it accepts the $arguments array that contains our context- related dependencies we save the aliases in the inner $arguments array as well. now on they can be referenced with the ID of the parent service and context
![Page 19: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/19.jpg)
Let’s have the service accept the dependencies we defined. To do so, we have to define an interface. We can call it the
DynamicArgumentsInterface.
![Page 20: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/20.jpg)
The following is an implementation of the definition for the ProcessorABDependant class:
Now we can modify the get(…) function in the ContextA-wareContainer, so that it can pass the dependencies we
defined to a service we want to retrieve:
![Page 21: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/21.jpg)
Explanation:
the beginning is the same – we start with retrieving our service of choice if the DynamicArgumentsInterface is implemented, we get all of our dependencies and pass them as an array to the setDynamicsArguments(...) function
if the DynamicArgumentsInterface is not implemented, the logic remains the same
![Page 22: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/22.jpg)
The final step is to have the container compiler read all of the dynamic arguments and pass them to the ContextAwareContainer.
Let’s expand the ContextSensitiveServicesCompilerPass to reflect this.
![Page 23: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/23.jpg)
Explanation:
to register the calling of the set(…) method, we retrieve the proper- ties option of the service that is currently being processed next, we read its dynamic_arguments parameter we finish by adding it as the last argument of the set(...) function
![Page 24: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/24.jpg)
This is how we go about defining dependencies between services that automatically adjust to a given context.
Just a bit of effort and a few simple classes allowed us to create a functionality that expands on what can be achieved with the Dependency Injection component. The result is convenient to use and achieves a high level of abstraction.
![Page 25: Service and context - building a context-dependent API](https://reader031.vdocuments.us/reader031/viewer/2022020314/58ecc3001a28abd3558b45f3/html5/thumbnails/25.jpg)
Find the code from this article in my repo:
I hope you enjoyed the presentation. If you have any feedback, or you believe that there is a better way to solve this issue, please contact me at: