I personally dislike this practice, for two reasons:
- It makes discovery of the factories more difficult. Where do I look — in a
Factory
directory under the current one? in a Service
directory under the current one? Or are those up a level, i.e., in a peer namespace? If so, they then might contain factories for multiple namespaces, which can also be confusing.
- It makes writing the factories harder. When the class the factory creates is in the same namespace, you don’t need to import it. When it’s in a sibling namespace, you need to import it. When it’s in a subnamespace, you have redundant information in the class name:
Factory\SomeMiddlewareFactory
or Service\SomeMiddlewareFactory
.
Which brings me to a couple more notes.
First, in your example, you have things like the following:
|-- AuthenticationMiddleware.php
|-- Service
|-- AuthenticationFactory.php
This is confusing, as it’s unclear from the name whether AuthenticationFactory
will create an instance of the AuthenticationMiddleware
, or some nebulous Authentication
class. The factories should clearly indicate what they create.
Second, the name Service
is nebulous, particularly for somebody not familiar with Zend Framework in particular. Does it refer to something accessing a web service? Or is it something that provides a particular web service? or is it some other type of service entirely? To answer the question, it’s the latter. In your example, it’s referring to a “service” registered in a “service manager”, which is what ZF calls its dependency injection container. In Enrico’s original post, it’s actually referring to domain models and similar.
If we consider your example, where the Service
directory and subnamespace is for seeding the DI container, if we consider the fact that Expressive is a PSR-11 consumer, which defines a ContainerInterface
, and the classes in these directories provide factories by which the container can retrieve dependencies, Factory
is a more clear namespace if you really, really need to separate such classes. As noted earlier, I think it’s far better to keep them together in the same namespace.
In Enrico’s example, I’d argue that the directory/namespace should be Model
or Domain
, if it needs to exist at all. Many of these classes are often the core of a module. As a quick example, let’s say I’ve created a module for a web service that will list books in an inventory, and allow manipulating the inventory. My basic entities might be a Book
class, a Collection
(for sets of books), a Repository
(for retrieving books from my persistent storage), and an InputFilter
(for validating new and/or updated book entries submitted to the service). These could be in the root of the module. I might then have a Middleware
subdirectory/subnamespace that contains the various middleware for the web service itself, and which uses these classes:
Books
|-- src
|-- Book.php
|-- Collection.php
|-- ConfigProvider.php
|-- InputFilter.php
|-- Repository.php
|-- Middleware
|-- AddBookMiddleware.php
|-- BookMiddleware.php
|-- SearchMiddleware.php
|-- UpdateBookMiddleware.php
|-- templates
|-- test
All that said, though, I think what Enrico is trying to get at is having a suggested, recommended directory structure for modules that is predictable. This makes it easier for developers working on an existing module for the first time to understand the structure: they know where to find classes that provide specific areas of functionality, and where to put new classes, based on their functionality. Additionally, it allows us to have our tooling that creates modules create these standard subdirectories, eliminating the need for developers to create their own structure. If this is what we want to provide, then a Domain
or Model
subnamespace would make sense.
Of course, any of these subnamespaces may be omitted if the module does not provide classes that fill those roles.
Finally, I’ll throw this out there, as it’s a discussion I’ve had with @enrico and others a few times the past month:
The term middleware should refer to any middleware that delegates processing of the request in one or more branches of its logic. The terms action or handler should refer to middleware that never delegates processing, and, instead, always generates and returns a response on its own.
With that in mind, we’d have Middleware
and/or Action
(or Handler
) subnamespaces, depending on what functionality the module exposes.