Lazy services for ServiceManager

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Lazy services for ServiceManager

Marco Pivetta
Hi everybody,

I've just finished up a first PoC (tested and working) of Lazy Services for the ServiceManager component at https://github.com/zendframework/zf2/pull/2995.

I was asked by Maks3w to ping the mailing list for suggestions/thoughts about this idea, since it is a major feature in the ServiceManager which also introduces a soft dependency from ServiceManager to doctrine/common (2.4, so not yet available).

Basically, my aim is containing service location, which is spreading around users of ZF2 at an incredible speed, and which I consider to be a harmful approach to lazy initialization of dependencies (because of hardcoded service names, hard mockability, dependency of classes to a service locator).

I think the proxy approach solves the problem in a quite elegant way, by abstracting the problem of lazy initialization away and giving you back the performance you were craving when you were thinking "let's make this ServiceLocatorAwareInterface".

Problems I still have to solve in my pull request are following:

 1. should this implementation consider serialization of services? It's usually considered bad practice, so I don't know if a lazy service should support serialization.
 2. what happens when services get updated and proxies don't? I didn't implement a mechanism to flush all existing proxies, nor I have an idea how to do that. I could really appreciate hints on this one...
 3. I'm still working on cloning services, but this should be trivial.

I hope you like the idea!

Cheers,

Marco Pivetta

http://twitter.com/Ocramius     

http://ocramius.github.com/

Reply | Threaded
Open this post in threaded view
|

Re: Lazy services for ServiceManager

Gregory
That's some real nice work!

Can you provide a dump of the classes that the proxy object extends and implements, my concern here, is that the proxy object is not usable in type checks, hints, or even shared event listeners?




On Sat, Nov 17, 2012 at 9:03 AM, Marco Pivetta <[hidden email]> wrote:
Hi everybody,

I've just finished up a first PoC (tested and working) of Lazy Services for the ServiceManager component at https://github.com/zendframework/zf2/pull/2995.

I was asked by Maks3w to ping the mailing list for suggestions/thoughts about this idea, since it is a major feature in the ServiceManager which also introduces a soft dependency from ServiceManager to doctrine/common (2.4, so not yet available).

Basically, my aim is containing service location, which is spreading around users of ZF2 at an incredible speed, and which I consider to be a harmful approach to lazy initialization of dependencies (because of hardcoded service names, hard mockability, dependency of classes to a service locator).

I think the proxy approach solves the problem in a quite elegant way, by abstracting the problem of lazy initialization away and giving you back the performance you were craving when you were thinking "let's make this ServiceLocatorAwareInterface".

Problems I still have to solve in my pull request are following:

 1. should this implementation consider serialization of services? It's usually considered bad practice, so I don't know if a lazy service should support serialization.
 2. what happens when services get updated and proxies don't? I didn't implement a mechanism to flush all existing proxies, nor I have an idea how to do that. I could really appreciate hints on this one...
 3. I'm still working on cloning services, but this should be trivial.

I hope you like the idea!

Cheers,

Marco Pivetta

http://twitter.com/Ocramius     

http://ocramius.github.com/




--
Greg
Reply | Threaded
Open this post in threaded view
|

Re: Lazy services for ServiceManager

Marco Pivetta
This post has NOT been accepted by the mailing list yet.
Hi there!
That's all things included in my tests and in doctrine/common#168 (also in the tests for that one) :)

I added a link to a generated proxy class (with the original one nearby) in the PR page :)

Marco Pivetta

http://twitter.com/Ocramius     

http://ocramius.github.com/



On 17 November 2012 19:30, Gregory [via Zend Framework Community] <[hidden email]> wrote:
That's some real nice work!

Can you provide a dump of the classes that the proxy object extends and implements, my concern here, is that the proxy object is not usable in type checks, hints, or even shared event listeners?




On Sat, Nov 17, 2012 at 9:03 AM, Marco Pivetta <[hidden email]> wrote:
Hi everybody,

I've just finished up a first PoC (tested and working) of Lazy Services for the ServiceManager component at https://github.com/zendframework/zf2/pull/2995.

I was asked by Maks3w to ping the mailing list for suggestions/thoughts about this idea, since it is a major feature in the ServiceManager which also introduces a soft dependency from ServiceManager to doctrine/common (2.4, so not yet available).

Basically, my aim is containing service location, which is spreading around users of ZF2 at an incredible speed, and which I consider to be a harmful approach to lazy initialization of dependencies (because of hardcoded service names, hard mockability, dependency of classes to a service locator).

I think the proxy approach solves the problem in a quite elegant way, by abstracting the problem of lazy initialization away and giving you back the performance you were craving when you were thinking "let's make this ServiceLocatorAwareInterface".

Problems I still have to solve in my pull request are following:

 1. should this implementation consider serialization of services? It's usually considered bad practice, so I don't know if a lazy service should support serialization.
 2. what happens when services get updated and proxies don't? I didn't implement a mechanism to flush all existing proxies, nor I have an idea how to do that. I could really appreciate hints on this one...
 3. I'm still working on cloning services, but this should be trivial.

I hope you like the idea!

Cheers,

Marco Pivetta

http://twitter.com/Ocramius     

http://ocramius.github.com/




--
Greg



If you reply to this email, your message will be added to the discussion below:
http://zend-framework-community.634137.n4.nabble.com/Lazy-services-for-ServiceManager-tp4658143p4658144.html
To start a new topic under ZF Contributor, email [hidden email]
To unsubscribe from ZF Contributor, click here.
NAML

Reply | Threaded
Open this post in threaded view
|

Re: Lazy services for ServiceManager

Marco Pivetta
In reply to this post by Gregory
Hi there!
That's all things included in my tests and in doctrine/common#168 (also in the tests for that one) :)

I added a link to a generated proxy class (with the original one nearby) in the PR page (https://github.com/zendframework/zf2/pull/2995) :)

Marco Pivetta

http://twitter.com/Ocramius     

http://ocramius.github.com/



On 17 November 2012 19:29, Greg <[hidden email]> wrote:
That's some real nice work!

Can you provide a dump of the classes that the proxy object extends and implements, my concern here, is that the proxy object is not usable in type checks, hints, or even shared event listeners?




On Sat, Nov 17, 2012 at 9:03 AM, Marco Pivetta <[hidden email]> wrote:
Hi everybody,

I've just finished up a first PoC (tested and working) of Lazy Services for the ServiceManager component at https://github.com/zendframework/zf2/pull/2995.

I was asked by Maks3w to ping the mailing list for suggestions/thoughts about this idea, since it is a major feature in the ServiceManager which also introduces a soft dependency from ServiceManager to doctrine/common (2.4, so not yet available).

Basically, my aim is containing service location, which is spreading around users of ZF2 at an incredible speed, and which I consider to be a harmful approach to lazy initialization of dependencies (because of hardcoded service names, hard mockability, dependency of classes to a service locator).

I think the proxy approach solves the problem in a quite elegant way, by abstracting the problem of lazy initialization away and giving you back the performance you were craving when you were thinking "let's make this ServiceLocatorAwareInterface".

Problems I still have to solve in my pull request are following:

 1. should this implementation consider serialization of services? It's usually considered bad practice, so I don't know if a lazy service should support serialization.
 2. what happens when services get updated and proxies don't? I didn't implement a mechanism to flush all existing proxies, nor I have an idea how to do that. I could really appreciate hints on this one...
 3. I'm still working on cloning services, but this should be trivial.

I hope you like the idea!

Cheers,

Marco Pivetta

http://twitter.com/Ocramius     

http://ocramius.github.com/




--
Greg

Reply | Threaded
Open this post in threaded view
|

Re: Lazy services for ServiceManager

Artur Bodera
In reply to this post by Marco Pivetta
On Sat, Nov 17, 2012 at 4:03 PM, Marco Pivetta <[hidden email]> wrote:
Basically, my aim is containing service location, which is spreading around users of ZF2 at an incredible speed, and which I consider to be a harmful approach to lazy initialization of dependencies (because of hardcoded service names, hard mockability, dependency of classes to a service locator).

I didn't quite got it, so I bugged Marco on sunday (oh us "nolifes" ;) and clarified what is this noise about ...

Example:

    class UsefulService implements ServiceManagerAware 
    {
        /* ... */
        public function doSomethingUseful() 
        {
             $this->serviceManager->get('otherService')->doSomething();
        }
    }



So basically using SM and "ServiceManagerAware" interface with your service classes (and methods) will give you these headaches:

1) You are making your service dependant on a service locator
Think about it - that breaks a few rules by itself but is also quite absurd - your service is now, by definition, coupled to some greater system.
 
2) Dependencies' names (other services) are hard coded deep inside your class
Whether you use "DB" or "database" or something super-specific, doesn't change the fact that it sits somewhere in your method or sub-dependency method as a string. You can't configure it, you can't change it on the fly and you are forcing this on other people/projets that might use your service (i.e. if you're writing a module) 

3) Unit-testing gets error-prone and quite complex.
You need to remember all deps that sit somewhere inside your method, you need to mock them properly, you can get errors from deep inside function call tree for things that you consider hard deps.



The proper way to write services (or any coupled objects) is to include hard deps in constructor - this ensures that there are no defunct objects with missing deps or having incorrect deps... ever. Sprinkle it with contract oriented design principle (define deps as interfaces) and you've got a winner.

    class UsefulService 
    {
        protected $other;

        public function __construct(OtherServiceInterface $other)
        {
            $this->other = $other;
        }

        public function doSomethingUseful() 
        {
             $this->other->doSomething();
        }
    }



The problem with above (having hard deps in constructor) is that, regardless if all or none of those deps are _actually_ used, they need to be consumed - hence instantiated. If class A depends on B and C, you need those latter in order to have an instance of A. This means that before you can start doing anything useful you've burned a lot of CPU cycles on instantiating services you might never use (i.e. because C is used in someMethodYouNeverCalled())

Marco's proxies will only instantiate hard dependencies when a method on the dependency is _actually called_. Only then will it create an instance of the object and proxy-call the actual method.


Performance wise - it's almost the same as having manual calls on $sm and fetching the dependency. The auto-generate proxy method will have similar code to a method which queries SM (which is a bad thing as described above).

Serialization - Marco wants to throw it in, but I personally don't see any use case, why would anyone serialize a service ? Services are stateless bags of methods and should not be serialized/stored. Serialization by definition is used to convert a value object or entity to a portable, storable format - but why would I need to store a service? Hence - I don't see any need for proxies to be serializable.

-- 
      __
     /.)\   +48 695 600 936
     \(./   [hidden email]