Choosing Zend Framework Service Manager Configuration Types: Factory, Abstract Factory, or Invokable
Guide to picking Zend Framework Service Manager configuration types: invokable, factory, or abstract factory, with a MailTransport factory example and validation steps.
12 Jan 2026, 14:26 UTC

Decision and constraints
When configuring services in Zend Framework 2/3 (or Laminas) you must decide how to define each service in the Service Manager. The decision hinges on two factors:
- Complexity of object creation – does the service need dependencies, configuration values, or conditional logic?
- Need for runtime parameters – will the service be instantiated with different arguments based on the request?
Constraints to keep in mind:
- Avoid the service‑locator anti‑pattern: factories should not store the container for later use.
- Prefer declarative configuration where possible to keep module.config.php readable.
- Remember that Zend Framework Service Manager is deprecated in favor of Laminas Service Manager; the same concepts apply, but verify version compatibility if you are migrating.
Supported options
| Option | When to Use | Configuration Effort | Flexibility |
|---|---|---|---|
| Invokable | Simple classes with no dependencies or configuration | Zero – just list the class name | Low – cannot inject dependencies or runtime values |
| Factory | A single service that needs dependencies, logic, or conditional construction | Medium – write a factory class or closure | High – full DI control, can access the container for dependencies only |
| Abstract Factory | Families of related services (e.g., multiple DB adapters, cache adapters) that share creation logic | Higher – implement match logic and service creation | Very High – reduces duplication across many similar services |
Trade‑offs
- Invokable – fastest to add, but if the class later gains a dependency you must refactor to a factory.
- Factory – provides explicit dependency injection and keeps the service locator out of your business code; the downside is extra boilerplate for each service.
- Abstract Factory – eliminates repetitive factory classes for a service family, but the matching logic can make it harder to trace which service is being resolved, especially when the family grows.
Concrete implementation: Factory for a MailTransport service
Assume we need a MailTransport that depends on a SmtpOptions object holding host, port, username, and password.
Factory class
return [
'service_manager' => [
'factories' => [
Application\Mail\MailTransport::class => \
Application\Factory\MailTransportFactory::class,
],
],
];
Validation in a unit test
use Zend\Config\Aggregator;
use Zend\Stdlib\ArrayUtils;
$aggregator = new Aggregator([
function () { return include 'config/autoload/global.php'; },
function () { return include 'config/autoload/local.{local,}.php'; },
]);
$config = $aggregator->getMergedConfig();
print_r($config['service_manager']['factories']);
Check that the expected factory class appears in the output.
Summary
Choose an invokable only for truly dependency‑free classes. Opt for a factory when a service needs one or more dependencies or conditional construction logic. Reserve an abstract factory for groups of services that share the same creation pattern, accepting the extra matching logic as the trade‑off for reduced duplication. Always validate that factories do not retain the container and that the service can be retrieved cleanly in a unit test or integration test.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.