service and context - building a context-dependent api

25
Service and context Building a context-dependent API By Michał Kurzeja

Upload: polcode

Post on 22-Jan-2018

307 views

Category:

Software


0 download

TRANSCRIPT

Page 2: Service and context - building a context-dependent API

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

...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

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

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

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

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

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

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

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

The definition of the class as a service:

Page 12: Service and context - building a context-dependent API

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

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

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

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

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

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

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

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

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

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

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

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

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

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:

[email protected]