Service Factories Inside Individual Components

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

Service Factories Inside Individual Components

ralphschindler
Hi all,

I'd like to find a consensus/decision as to where we should be placing
our service factories in the ZF codebase.

I propose we keep all service factories inside the Zend\Mvc, like:

https://github.com/zendframework/zf2/tree/master/library/Zend/Mvc/Service

(For a moment, I need you to forget that 1) ZF is generally installed as
a whole library, and 2) that composer makes it easy to solve the
"component dependency and installation" problem.  This is purely about
component architecture.)

Here's why:


1) Factories have dependencies that components (generally) do not...

While some components might use ServiceManager to facilitate their own
"plugin" needs, most do not.  Adding "application level" classes here
introduce new hard dependencies on components (mainly
Zend\ServiceManager and Zend\Mvc) that generally don't have to be here.


2) Service factories are for solving "application layer problems" not
"component layer problems".

Since Zend\Mvc solves the problem of application (layer) architecture,
service factories should reside there.  From a component API
perspective, one should *never* first feel the need to use the
ServiceManager to consume the component.  The first/primary use case for
any component (in a standalone / non-mvc workflow) simply should be
$instance = new Zend\Component\Component();  It's not until you want to
present certain instances as "services" in the MVC Application context
should one consider using the ServiceManager to facilitate this workflow.


3) Factories are generally useless without the MVC and/or Service
Manager plus known conventions...

Service managers are for wiring extra-component dependencies, not
intra-component dependencies.  As such, our Zend\Mvc promotes a small
set of "conventions" that must be followed for a smooth workflow.
Components themselves should be left *completely* convention free.  To
understand, see this code:

https://github.com/zendframework/zf2/blob/master/library/Zend/Db/Adapter/AdapterServiceFactory.php#L23-L28

Note a few things:

   - There is clear dependency on Zend\ServiceManager
   - There is a hidden dependency on there being a Service by the name
"Config" that must resolve to an ArrayAccess/array, *wherein* there must
be a key named 'db', that resolves to an array.
     - Put another way, there is a dependency on a convention here.
     - That convention is promoted by Zend\Mvc, nowhere else.


4) Moving forward, it should be clear to the consumer of Zend\Mvc what
is in place and available in case one wants to override the way in which
an instance is produced.  Placing factories in Zend\Mvc facilitates that
much better.




My suggestion for the 2.2 release would be to ensure that any new
service factories get introduced in the Zend\Mvc\Service namespace.
More over, service factory introduction really needs to be vetted, I am
not completely sure that they all belong in ZF2 proper.  Some of them
should probably exist in ZfCommons, perhaps in a ZfService module - at
least perhaps they should exist there first before being promoted to ZF2
proper.


Thoughts?

Ralph Schindler
Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

Vincent BLANCHON

"My suggestion for the 2.2 release would be to ensure that any new service factories get introduced in the Zend\Mvc\Service namespace"

+ 1 with you

" More over, service factory introduction really needs to be vetted, I am not completely sure that they all belong in ZF2 proper. Some of them should probably exist in ZfCommons, perhaps in a ZfService module"



Vincent BLANCHON
Expert PHP & Zend Framework
Ausy pour Orange Portail



2013/3/11 Ralph Schindler <[hidden email]>
Hi all,

I'd like to find a consensus/decision as to where we should be placing our service factories in the ZF codebase.

I propose we keep all service factories inside the Zend\Mvc, like:

https://github.com/zendframework/zf2/tree/master/library/Zend/Mvc/Service

(For a moment, I need you to forget that 1) ZF is generally installed as a whole library, and 2) that composer makes it easy to solve the "component dependency and installation" problem.  This is purely about component architecture.)

Here's why:


1) Factories have dependencies that components (generally) do not...

While some components might use ServiceManager to facilitate their own "plugin" needs, most do not.  Adding "application level" classes here introduce new hard dependencies on components (mainly Zend\ServiceManager and Zend\Mvc) that generally don't have to be here.


2) Service factories are for solving "application layer problems" not "component layer problems".

Since Zend\Mvc solves the problem of application (layer) architecture, service factories should reside there.  From a component API perspective, one should *never* first feel the need to use the ServiceManager to consume the component.  The first/primary use case for any component (in a standalone / non-mvc workflow) simply should be $instance = new Zend\Component\Component();  It's not until you want to present certain instances as "services" in the MVC Application context should one consider using the ServiceManager to facilitate this workflow.


3) Factories are generally useless without the MVC and/or Service Manager plus known conventions...

Service managers are for wiring extra-component dependencies, not intra-component dependencies.  As such, our Zend\Mvc promotes a small set of "conventions" that must be followed for a smooth workflow. Components themselves should be left *completely* convention free.  To understand, see this code:

https://github.com/zendframework/zf2/blob/master/library/Zend/Db/Adapter/AdapterServiceFactory.php#L23-L28

Note a few things:

  - There is clear dependency on Zend\ServiceManager
  - There is a hidden dependency on there being a Service by the name "Config" that must resolve to an ArrayAccess/array, *wherein* there must be a key named 'db', that resolves to an array.
    - Put another way, there is a dependency on a convention here.
    - That convention is promoted by Zend\Mvc, nowhere else.


4) Moving forward, it should be clear to the consumer of Zend\Mvc what is in place and available in case one wants to override the way in which an instance is produced.  Placing factories in Zend\Mvc facilitates that much better.




My suggestion for the 2.2 release would be to ensure that any new service factories get introduced in the Zend\Mvc\Service namespace. More over, service factory introduction really needs to be vetted, I am not completely sure that they all belong in ZF2 proper.  Some of them should probably exist in ZfCommons, perhaps in a ZfService module - at least perhaps they should exist there first before being promoted to ZF2 proper.


Thoughts?

Ralph Schindler

Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

weierophinney
Administrator
In reply to this post by ralphschindler
On Mon, Mar 11, 2013 at 11:53 AM, Ralph Schindler
<[hidden email]> wrote:

> I'd like to find a consensus/decision as to where we should be placing our
> service factories in the ZF codebase.
>
> I propose we keep all service factories inside the Zend\Mvc, like:
>
> https://github.com/zendframework/zf2/tree/master/library/Zend/Mvc/Service
>
> (For a moment, I need you to forget that 1) ZF is generally installed as a
> whole library, and 2) that composer makes it easy to solve the "component
> dependency and installation" problem.  This is purely about component
> architecture.)
>
> Here's why:
>
>
> 1) Factories have dependencies that components (generally) do not...
>
> While some components might use ServiceManager to facilitate their own
> "plugin" needs, most do not.  Adding "application level" classes here
> introduce new hard dependencies on components (mainly Zend\ServiceManager
> and Zend\Mvc) that generally don't have to be here.

I disagree. These should not be hard dependencies, but rather optional
ones, and properly belong in a "suggest" section of the component's.
As such, they are opt-in requirements, and provide a turnkey solution,
reducing the amount of code a developer needs.

More below.

> 2) Service factories are for solving "application layer problems" not
> "component layer problems".
>
> Since Zend\Mvc solves the problem of application (layer) architecture,
> service factories should reside there.

I disagree. Service factories may be used in application layers
outside of the framework's MVC. Having these factories in the
components themselves makes them re-usable across more layers, without
introducing an additional dependency. More below.

> From a component API perspective,
> one should *never* first feel the need to use the ServiceManager to consume
> the component.  The first/primary use case for any component (in a
> standalone / non-mvc workflow) simply should be $instance = new
> Zend\Component\Component();  It's not until you want to present certain
> instances as "services" in the MVC Application context should one consider
> using the ServiceManager to facilitate this workflow.

I don't think having a service factory changes that story at all. It
simply provides *OPTIONAL* support for the ServiceManager component,
with a specific paradigm for usage in the SM. Having the service
factory is *NOT* dictating its usage in the component -- it's giving
you the _option_ to use the component with the SM in a specific,
defined way.

It has nothing to do with Zend\Mvc, really. Zend\Mvc is simply
consuming those default factories.

> 3) Factories are generally useless without the MVC and/or Service Manager
> plus known conventions...

They _are_ useless without the SM -- but again, that's why they are
considered optional, and only work if you explicitly install that
dependency.

They are _not_ useless without the MVC, however.

As an example, let's say I want to use the SM in a custom
microframework -- and let's say, furthermore, that this microframework
is for processing message queues (so we can demonstrate a non-MVC
paradigm). I want to consume a variety of services: DB, Log,
Validators and Filters (as I'll be doing validation of incoming
parameters), and even View (because I might send some email). We
already have service factories for all of those -- which means I don't
have to write those. Moreover, they all follow a common paradigm when
it comes to dynamic service configuration -- something I want to
consider, as I want to test my microframework before deployment.

Because those factories exist, and they aren't tied to the MVC, I can
install only those components I need, plus the ServiceManager, and get
right to work. I don't have to write a bunch of other code.

It's not just about having a story for the MVC; it's about having a
cohesive, consistent story across components as to how they will
interact _by_ _default_ with the ServiceManager to make it easy for a
developer to consume them in that paradigm. Yes, you will _always_ be
able to write your own factories, and if you have a specific paradigm
you want to follow in your own application layer, go for it. However,
we provide a paradigm as a framework that you can subscribe to
immediately without any extra work.

> Service managers are for wiring extra-component dependencies, not
> intra-component dependencies.  As such, our Zend\Mvc promotes a small set of
> "conventions" that must be followed for a smooth workflow. Components
> themselves should be left *completely* convention free.

I totally disagree with this.

The primary benefit to having a ServiceManager is to allow for
configurable, dynamic services. Without that, we're stuck in
hard-coded configuration. What do I mean by that?

One solution put forth to making services dynamic/configurable is to
allow passing factory configuration to constructors. This defeats the
purpose as far as I'm concerned: I now have to know exactly where the
configuration for that factory lives -- which means I likely am unable
to override the configuration elsewhere -- and then have to have code
that looks like:

    $factory = new SomeFactory(include $pathToConfiguration);
    $services->setFactory('service-name', $factory);

That's painful. It's exactly the sort of code that makes for a lot of
repetition, and a lot of NIH and non-DRY meta-frameworks sitting on
top in order to do what we already can do in the framework now.

For those who are wondering what I'm talking about at this point,
Ralph is referring to the convention we have within ZF2 of a "Config"
service. The "Config" service is the fully-merged and populated
configuration post-module loading. Is it onerous to have a convention
of using a "Config" service?

I don't think so. Why?

* It's easy to educate about it. Any given component that provides a
service factory can say, "Your SM must contain a Config service of
type array. It needs a top-level key of "SuchAndSuch", with a value of
the following structure."
* As already noted, it simplifies the story of dynamically configuring
a factory. Yes, the factory now depends on a convention, but I'd call
that a lesser evil than programmatically configuring the factory.


> To understand, see
> this code:
>
> https://github.com/zendframework/zf2/blob/master/library/Zend/Db/Adapter/AdapterServiceFactory.php#L23-L28
>
> Note a few things:
>
>   - There is clear dependency on Zend\ServiceManager

For the factory, of course there will be. But, again:

* the dependency within the DB component should be _optional_ (i.e.,
marked as "suggest")
* usage of that factory is completely opt-in; if you don't want to use
it, ignore it.

>   - There is a hidden dependency on there being a Service by the name
> "Config" that must resolve to an ArrayAccess/array, *wherein* there must be
> a key named 'db', that resolves to an array.
>     - Put another way, there is a dependency on a convention here.
>     - That convention is promoted by Zend\Mvc, nowhere else.

The convention started early on in ZF2. Yes, it started from needing
to configure the MVC, but I myself have used the convention elsewhere
-- particularly in testing, as it is a very simple configuration, and
allows re-using existing SM configuration without needing to
completely replicate it. I realize you see this as an MVC definition,
but I'd urge you to think of it as simply an easy SM convention.

And, again, if you don't like that convention, or have other
conventions established in your application, you can completely ignore
it and write/provide your own factories. But as a framework, we need a
consistent way to define configurable services, and this is an
expedient way to do so.

> 4) Moving forward, it should be clear to the consumer of Zend\Mvc what is in
> place and available in case one wants to override the way in which an
> instance is produced.  Placing factories in Zend\Mvc facilitates that much
> better.

I disagree here, too.

We provide the list of factories by adding the factories to the
configuration. Since adding factories is inexpensive (the expense
typically comes only when they are invoked), we should have default
entries for all factories we provide and/or all factories which might
be of use in a vanilla MVC application. A page in the manual can be
used to provide a list of all factories, with links to the
documentation on usage/configuration of each factory.

> My suggestion for the 2.2 release would be to ensure that any new service
> factories get introduced in the Zend\Mvc\Service namespace. More over,
> service factory introduction really needs to be vetted, I am not completely
> sure that they all belong in ZF2 proper.  Some of them should probably exist
> in ZfCommons, perhaps in a ZfService module - at least perhaps they should
> exist there first before being promoted to ZF2 proper.

I agree that they need to be vetted -- but that's also what the pull
request system is for.

And, for the record, in case it wasn't clear, I disagree entirely. I
think the factories should be in the components themselves. In cases
where we have marked the SM as a required dependency due to the
presence of a factory, we should modify the dependency to be optional
only.


--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

Sascha-Oliver Prolic
1+ Matthew, definetly agree with you, couldn't have said better.


2013/3/11 Matthew Weier O'Phinney <[hidden email]>
On Mon, Mar 11, 2013 at 11:53 AM, Ralph Schindler
<[hidden email]> wrote:
> I'd like to find a consensus/decision as to where we should be placing our
> service factories in the ZF codebase.
>
> I propose we keep all service factories inside the Zend\Mvc, like:
>
> https://github.com/zendframework/zf2/tree/master/library/Zend/Mvc/Service
>
> (For a moment, I need you to forget that 1) ZF is generally installed as a
> whole library, and 2) that composer makes it easy to solve the "component
> dependency and installation" problem.  This is purely about component
> architecture.)
>
> Here's why:
>
>
> 1) Factories have dependencies that components (generally) do not...
>
> While some components might use ServiceManager to facilitate their own
> "plugin" needs, most do not.  Adding "application level" classes here
> introduce new hard dependencies on components (mainly Zend\ServiceManager
> and Zend\Mvc) that generally don't have to be here.

I disagree. These should not be hard dependencies, but rather optional
ones, and properly belong in a "suggest" section of the component's.
As such, they are opt-in requirements, and provide a turnkey solution,
reducing the amount of code a developer needs.

More below.

> 2) Service factories are for solving "application layer problems" not
> "component layer problems".
>
> Since Zend\Mvc solves the problem of application (layer) architecture,
> service factories should reside there.

I disagree. Service factories may be used in application layers
outside of the framework's MVC. Having these factories in the
components themselves makes them re-usable across more layers, without
introducing an additional dependency. More below.

> From a component API perspective,
> one should *never* first feel the need to use the ServiceManager to consume
> the component.  The first/primary use case for any component (in a
> standalone / non-mvc workflow) simply should be $instance = new
> Zend\Component\Component();  It's not until you want to present certain
> instances as "services" in the MVC Application context should one consider
> using the ServiceManager to facilitate this workflow.

I don't think having a service factory changes that story at all. It
simply provides *OPTIONAL* support for the ServiceManager component,
with a specific paradigm for usage in the SM. Having the service
factory is *NOT* dictating its usage in the component -- it's giving
you the _option_ to use the component with the SM in a specific,
defined way.

It has nothing to do with Zend\Mvc, really. Zend\Mvc is simply
consuming those default factories.

> 3) Factories are generally useless without the MVC and/or Service Manager
> plus known conventions...

They _are_ useless without the SM -- but again, that's why they are
considered optional, and only work if you explicitly install that
dependency.

They are _not_ useless without the MVC, however.

As an example, let's say I want to use the SM in a custom
microframework -- and let's say, furthermore, that this microframework
is for processing message queues (so we can demonstrate a non-MVC
paradigm). I want to consume a variety of services: DB, Log,
Validators and Filters (as I'll be doing validation of incoming
parameters), and even View (because I might send some email). We
already have service factories for all of those -- which means I don't
have to write those. Moreover, they all follow a common paradigm when
it comes to dynamic service configuration -- something I want to
consider, as I want to test my microframework before deployment.

Because those factories exist, and they aren't tied to the MVC, I can
install only those components I need, plus the ServiceManager, and get
right to work. I don't have to write a bunch of other code.

It's not just about having a story for the MVC; it's about having a
cohesive, consistent story across components as to how they will
interact _by_ _default_ with the ServiceManager to make it easy for a
developer to consume them in that paradigm. Yes, you will _always_ be
able to write your own factories, and if you have a specific paradigm
you want to follow in your own application layer, go for it. However,
we provide a paradigm as a framework that you can subscribe to
immediately without any extra work.

> Service managers are for wiring extra-component dependencies, not
> intra-component dependencies.  As such, our Zend\Mvc promotes a small set of
> "conventions" that must be followed for a smooth workflow. Components
> themselves should be left *completely* convention free.

I totally disagree with this.

The primary benefit to having a ServiceManager is to allow for
configurable, dynamic services. Without that, we're stuck in
hard-coded configuration. What do I mean by that?

One solution put forth to making services dynamic/configurable is to
allow passing factory configuration to constructors. This defeats the
purpose as far as I'm concerned: I now have to know exactly where the
configuration for that factory lives -- which means I likely am unable
to override the configuration elsewhere -- and then have to have code
that looks like:

    $factory = new SomeFactory(include $pathToConfiguration);
    $services->setFactory('service-name', $factory);

That's painful. It's exactly the sort of code that makes for a lot of
repetition, and a lot of NIH and non-DRY meta-frameworks sitting on
top in order to do what we already can do in the framework now.

For those who are wondering what I'm talking about at this point,
Ralph is referring to the convention we have within ZF2 of a "Config"
service. The "Config" service is the fully-merged and populated
configuration post-module loading. Is it onerous to have a convention
of using a "Config" service?

I don't think so. Why?

* It's easy to educate about it. Any given component that provides a
service factory can say, "Your SM must contain a Config service of
type array. It needs a top-level key of "SuchAndSuch", with a value of
the following structure."
* As already noted, it simplifies the story of dynamically configuring
a factory. Yes, the factory now depends on a convention, but I'd call
that a lesser evil than programmatically configuring the factory.


> To understand, see
> this code:
>
> https://github.com/zendframework/zf2/blob/master/library/Zend/Db/Adapter/AdapterServiceFactory.php#L23-L28
>
> Note a few things:
>
>   - There is clear dependency on Zend\ServiceManager

For the factory, of course there will be. But, again:

* the dependency within the DB component should be _optional_ (i.e.,
marked as "suggest")
* usage of that factory is completely opt-in; if you don't want to use
it, ignore it.

>   - There is a hidden dependency on there being a Service by the name
> "Config" that must resolve to an ArrayAccess/array, *wherein* there must be
> a key named 'db', that resolves to an array.
>     - Put another way, there is a dependency on a convention here.
>     - That convention is promoted by Zend\Mvc, nowhere else.

The convention started early on in ZF2. Yes, it started from needing
to configure the MVC, but I myself have used the convention elsewhere
-- particularly in testing, as it is a very simple configuration, and
allows re-using existing SM configuration without needing to
completely replicate it. I realize you see this as an MVC definition,
but I'd urge you to think of it as simply an easy SM convention.

And, again, if you don't like that convention, or have other
conventions established in your application, you can completely ignore
it and write/provide your own factories. But as a framework, we need a
consistent way to define configurable services, and this is an
expedient way to do so.

> 4) Moving forward, it should be clear to the consumer of Zend\Mvc what is in
> place and available in case one wants to override the way in which an
> instance is produced.  Placing factories in Zend\Mvc facilitates that much
> better.

I disagree here, too.

We provide the list of factories by adding the factories to the
configuration. Since adding factories is inexpensive (the expense
typically comes only when they are invoked), we should have default
entries for all factories we provide and/or all factories which might
be of use in a vanilla MVC application. A page in the manual can be
used to provide a list of all factories, with links to the
documentation on usage/configuration of each factory.

> My suggestion for the 2.2 release would be to ensure that any new service
> factories get introduced in the Zend\Mvc\Service namespace. More over,
> service factory introduction really needs to be vetted, I am not completely
> sure that they all belong in ZF2 proper.  Some of them should probably exist
> in ZfCommons, perhaps in a ZfService module - at least perhaps they should
> exist there first before being promoted to ZF2 proper.

I agree that they need to be vetted -- but that's also what the pull
request system is for.

And, for the record, in case it wasn't clear, I disagree entirely. I
think the factories should be in the components themselves. In cases
where we have marked the SM as a required dependency due to the
presence of a factory, we should modify the dependency to be optional
only.


--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc



--
Sascha-Oliver Prolic
Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

Gregory
For what its worth I think Ralph's suggestion is a cleaner suggestion. If I'm understanding, it boils down to two things


[1] Defining the service that is supported by the ServiceManager
[2] Providing a factory for the service.

Both of these should occur under the ServiceManager component.

Even though the service factory could be placed within each component, there is still a need to register it with the ServiceManager either via the config or as a ZF2 available service (e.g Zend\Mvc\Service\ServiceListenerFactory). At which point the factory might as well be maintained the ServiceManager component.

For example, lets say I want to use the Db component, I'm really lazy, or trying to do a quick hack and need to get the Db component up and running? The minimum requirements should be the configuration array/object and nothing else. Having to stop to consider and configure the ServiceManager in order to initialize the Db should not be a requirement, and forcing me to have to analyse the service factory in order to determine its dependencies will just be irritating.

The best I can think of at present is that each component could have a Factory class with its dependencies clearly defined as method arguments, eg

Db\Adapter\AdapterFactory::create(array $config)

Not only does this mean an adapter can be instantiated with minimal (and relevant) dependencies, but also (IMHO), from a testing pointing of view it means we're not testing the service manager.

However, for a typical ZF2 app, a more convenient implementation of these factories is needed, and these factories would reside within the ServiceManager namespace. Not only would these service factories be expected to depend on the ServiceManager, but from a testing pointing of view (IMHO), these tests would be more about the ServiceManager.

Thus far, what has not been mentioned is the ModuleManager. The default services created by the ModuleManager have factory methods which do not follow the implementation style of the other service factories discussed so far. These default components are configured outside of the factory method once the factory has instantiated the component. It would be better if these (slightly more) ModuleManager specific factories reside within the ModuleManager namespace and maybe have different factory names so that their corresponding standard ZF2 service factories and be used just like the rest.


Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

jeremiah
In reply to this post by ralphschindler
Interesting discussion. Personally, I find the SM factory interface hard to love, because the classes have to hide their own SM requirements inside. Using Db as an example, I really rarely want to use the factory class over my own closure, because what if I need multiple configs that claim 'db' as a top level SM config handle? I'm most comfortable with closures in Module.php (because they seem awkward in module.config.php).

So I think where they live is perhaps mostly moot, but given the choice, I've think I agree with Matthew, that they belong in the component. If nothing else they can serve as an example of how to define the factory as a closure.

Jeremiah


Greg <[hidden email]> wrote:

For what its worth I think Ralph's suggestion is a cleaner suggestion. If I'm understanding, it boils down to two things


[1] Defining the service that is supported by the ServiceManager
[2] Providing a factory for the service.

Both of these should occur under the ServiceManager component.

Even though the service factory could be placed within each component, there is still a need to register it with the ServiceManager either via the config or as a ZF2 available service (e.g Zend\Mvc\Service\ServiceListenerFactory). At which point the factory might as well be maintained the ServiceManager component.

For example, lets say I want to use the Db component, I'm really lazy, or trying to do a quick hack and need to get the Db component up and running? The minimum requirements should be the configuration array/object and nothing else. Having to stop to consider and configure the ServiceManager in order to initialize the Db should not be a requirement, and forcing me to have to analyse the service factory in order to determine its dependencies will just be irritating.

The best I can think of at present is that each component could have a Factory class with its dependencies clearly defined as method arguments, eg

Db\Adapter\AdapterFactory::create(array $config)

Not only does this mean an adapter can be instantiated with minimal (and relevant) dependencies, but also (IMHO), from a testing pointing of view it means we're not testing the service manager.

However, for a typical ZF2 app, a more convenient implementation of these factories is needed, and these factories would reside within the ServiceManager namespace. Not only would these service factories be expected to depend on the ServiceManager, but from a testing pointing of view (IMHO), these tests would be more about the ServiceManager.

Thus far, what has not been mentioned is the ModuleManager. The default services created by the ModuleManager have factory methods which do not follow the implementation style of the other service factories discussed so far. These default components are configured outside of the factory method once the factory has instantiated the component. It would be better if these (slightly more) ModuleManager specific factories reside within the ModuleManager namespace and maybe have different factory names so that their corresponding standard ZF2 service factories and be used just like the rest.


Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

weierophinney
Administrator
On Tue, Mar 12, 2013 at 3:17 AM, Jeremiah Small <[hidden email]> wrote:
> Interesting discussion. Personally, I find the SM factory interface hard to
> love, because the classes have to hide their own SM requirements inside.
> Using Db as an example, I really rarely want to use the factory class over
> my own closure, because what if I need multiple configs that claim 'db' as a
> top level SM config handle? I'm most comfortable with closures in Module.php
> (because they seem awkward in module.config.php).

There's actually a really nice solution to this that was proposed for
both forms and loggers recently -- utilize an abstract factory. As an
example, look at this PR:

- https://github.com/zendframework/zf2/pull/3928

For those not following the link, it suggests using a top-level,
factory specific key, with a value that is a map of "service" name
mapping to the configuration to use in the factory. This mitigates the
problem of wanting multiple different connections nicely, and I think
it would be a good fit for a number of components, including DB.

> So I think where they live is perhaps mostly moot, but given the choice,
> I've think I agree with Matthew, that they belong in the component. If
> nothing else they can serve as an example of how to define the factory as a
> closure.
>
> Jeremiah
>
>
> Greg <[hidden email]> wrote:
>
> For what its worth I think Ralph's suggestion is a cleaner suggestion. If
> I'm understanding, it boils down to two things
>
>
> [1] Defining the service that is supported by the ServiceManager
> [2] Providing a factory for the service.
>
> Both of these should occur under the ServiceManager component.
>
> Even though the service factory could be placed within each component, there
> is still a need to register it with the ServiceManager either via the config
> or as a ZF2 available service (e.g Zend\Mvc\Service\ServiceListenerFactory).
> At which point the factory might as well be maintained the ServiceManager
> component.
>
> For example, lets say I want to use the Db component, I'm really lazy, or
> trying to do a quick hack and need to get the Db component up and running?
> The minimum requirements should be the configuration array/object and
> nothing else. Having to stop to consider and configure the ServiceManager in
> order to initialize the Db should not be a requirement, and forcing me to
> have to analyse the service factory in order to determine its dependencies
> will just be irritating.
>
> The best I can think of at present is that each component could have a
> Factory class with its dependencies clearly defined as method arguments, eg
>
> Db\Adapter\AdapterFactory::create(array $config)
>
> Not only does this mean an adapter can be instantiated with minimal (and
> relevant) dependencies, but also (IMHO), from a testing pointing of view it
> means we're not testing the service manager.
>
> However, for a typical ZF2 app, a more convenient implementation of these
> factories is needed, and these factories would reside within the
> ServiceManager namespace. Not only would these service factories be expected
> to depend on the ServiceManager, but from a testing pointing of view (IMHO),
> these tests would be more about the ServiceManager.
>
> Thus far, what has not been mentioned is the ModuleManager. The default
> services created by the ModuleManager have factory methods which do not
> follow the implementation style of the other service factories discussed so
> far. These default components are configured outside of the factory method
> once the factory has instantiated the component. It would be better if these
> (slightly more) ModuleManager specific factories reside within the
> ModuleManager namespace and maybe have different factory names so that their
> corresponding standard ZF2 service factories and be used just like the rest.
>
>



--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

ralphschindler

> There's actually a really nice solution to this that was proposed for
> both forms and loggers recently -- utilize an abstract factory. As an
> example, look at this PR:
>
> - https://github.com/zendframework/zf2/pull/3928
>
> For those not following the link, it suggests using a top-level,
> factory specific key, with a value that is a map of "service" name
> mapping to the configuration to use in the factory. This mitigates the
> problem of wanting multiple different connections nicely, and I think
> it would be a good fit for a number of components, including DB.
>
>> So I think where they live is perhaps mostly moot, but given the choice,
>> I've think I agree with Matthew, that they belong in the component. If

Right, for the record, I like the idea as well, I am just arguing about
placement.  This helps solve the "throw some conventional configuration
into a file, and out comes an instance from the service manager by a
specific name" problem.  This is immensely useful.

The only difference here, between my proposal and Matthew's proposal for
having these factories in the component they are assisting creation for,
is actual placement.

The difference is purely philosophical.  I prefer to keep actual
components with a small amount of dependencies and a small amount of
responsibilities.  Then I also prefer other components to do the
bridging (which is why I'd argue for Zend\Mvc or even a separate
component (perhaps Zend\Application[\]Service, or whatever) that does
all the bridging between the service manager and the component.

This way, when people look at components like Log or Form, they are not
immediately presented with classes like:

   Zend\Form\FormAbstractServiceFactory
   Zend\Log\LogAbstractServiceFactory
   Zend\Db\AdapterServiceFactory

When the standalone use case is as simple as:

   $form = new Zend\Form\Form('contact'); $form->add()
   $log = new Zend\Log\Logger; $log->addWriter(...)
   $db = new Zend\Db\Adapter\Adatper($cnxArray);

Service Factories, to me, are for being able to create instances in the
context of an application that consumes a ServiceManager/(ServiceLocator).

Again, there is inherently nothing wrong with putting them in the
components, I just find that it homogenizes the individual components
with ServiceManager / Application specific service responsibilities and
I *prefer* it be somewhere else instead of nestled in the components
API.  Either way, it's all preference.

-ralph


Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

jeremiah
On Mar 12, 2013, at 9:25 AM, Ralph Schindler wrote:

Again, there is inherently nothing wrong with putting them in the components, I just find that it homogenizes the individual components with ServiceManager / Application specific service responsibilities and I *prefer* it be somewhere else instead of nestled in the components API.  Either way, it's all preference.

Yes, I see certain logic in both proposals. As it is down to essentially aesthetics, one thing that should probably be considered is which location promotes optimal discoverability for people new to the framework and/or component. A factory class does seem to present a good model for how to configure it, even if you're just going to do your own closure, or not use ServiceManager at all. I think your example is arguably an illustration of increased discoverability, although as you say, it is a matter of preference as to if that is simultaneously clutter:

   Zend\Form\FormAbstractServiceFactory 
   Zend\Log\LogAbstractServiceFactory 
   Zend\Db\AdapterServiceFactory 

First, are we really talking about providing abstract factories for everything? Wouldn't we just make concrete factories for specific classes which implement Zend\ServiceManager\FactoryInterface?

Second, we don't consider it clutter when we see specific (optional) implementations of other things inside a class package though. For example we don't only have an AbstractAdapter. We provide several optional concrete implementations, and Http further provides multiple options for ResolverInterface:

    Zend\Authentication\Adapter\DbTable
    Zend\Authentication\Adapter\Digest
    Zend\Authentication\Adapter\Http
    Zend\Authentication\Adapter\Ldap

It seems to me that including optional Service support similar. If we follow the Authentication example, and we wanted to add a service factory for every type of concrete adapter, in Matthew's proposal we'd probably add the factory for each class in that class's namespace. Example:

    Zend\Authentication\Adapter\DbTable\ServiceFactory
    Zend\Authentication\Adapter\Digest\ServiceFactory
    Zend\Authentication\Adapter\Http\ServiceFactory
    Zend\Authentication\Adapter\Ldap\ServiceFactory

Side note, and again, probably just preference, but I prefer not re-stating the class name in the service factory class name, as the namespace already handles that. I realize that we already have started with names like Zend\Db\Adapter\AdapterServiceFactory (and many other examples of classes that restate the namespace) but this seems redundant to me, and I understand the argument for it, so ultimately I'm not really bothered either way.

Jeremiah
Reply | Threaded
Open this post in threaded view
|

Re: Service Factories Inside Individual Components

weierophinney
Administrator
On Tue, Mar 12, 2013 at 1:53 PM, Jeremiah Small <[hidden email]> wrote:
> On Mar 12, 2013, at 9:25 AM, Ralph Schindler wrote:
>
> Again, there is inherently nothing wrong with putting them in the
> components, I just find that it homogenizes the individual components with
> ServiceManager / Application specific service responsibilities and I
> *prefer* it be somewhere else instead of nestled in the components API.
> Either way, it's all preference.

It is. One argument I'd like to make, however, is that we already have
established a precedent of putting the service factories with the
components. Making a change now introduces inconsistency. If we want
to make a change, we should do so at 3.0. I'm still not convinced a
change is necessary.

> Yes, I see certain logic in both proposals. As it is down to essentially
> aesthetics, one thing that should probably be considered is which location
> promotes optimal discoverability for people new to the framework and/or
> component. A factory class does seem to present a good model for how to
> configure it, even if you're just going to do your own closure, or not use
> ServiceManager at all. I think your example is arguably an illustration of
> increased discoverability, although as you say, it is a matter of preference
> as to if that is simultaneously clutter:
>
>    Zend\Form\FormAbstractServiceFactory
>    Zend\Log\LogAbstractServiceFactory
>    Zend\Db\AdapterServiceFactory
>
> First, are we really talking about providing abstract factories for
> everything? Wouldn't we just make concrete factories for specific classes
> which implement Zend\ServiceManager\FactoryInterface?

They aren't actually abstract; they are implementations of
AbstractFactoryInterface. There's a difference in approach between
FactoryInterface and AbstractFactoryInterface; the former is a 1:1 map
between a service name and an instance, while the latter is an N:N
map. The AbstractFactory approach allows an easier way to setup
multiple instances of the same service type, as you can have a hashmap
of service names to configuration, but use the same logic for
instantiation, and register a single abstract factory instead of
multiple factory instances.


> Second, we don't consider it clutter when we see specific (optional)
> implementations of other things inside a class package though. For example
> we don't only have an AbstractAdapter. We provide several optional concrete
> implementations, and Http further provides multiple options for
> ResolverInterface:
>
>     Zend\Authentication\Adapter\DbTable
>     Zend\Authentication\Adapter\Digest
>     Zend\Authentication\Adapter\Http
>     Zend\Authentication\Adapter\Ldap
>
> It seems to me that including optional Service support similar. If we follow
> the Authentication example, and we wanted to add a service factory for every
> type of concrete adapter, in Matthew's proposal we'd probably add the
> factory for each class in that class's namespace.

You're comparing apples to oranges here. In the case of components
with adapters/plugins/helpers/etc, we provide implementations of
AbstractPluginManager (which is a specialized from of the
ServiceManager) to manage those *already*. This is because they are an
implementation detail of that given component usually.
(Zend\Authentication is one place we _don't_ do this, and probably
should.) We also currently provide a way in
Zend\Mvc/Zend\ModuleManager to configure those via the application
configuration.


--
Matthew Weier O'Phinney
Project Lead            | [hidden email]
Zend Framework          | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc