Support for "modules", similar to zend expressive?

Zend expressive has a great feature that allows applications to separate routes into “modules”: https://docs.zendframework.com/zend-expressive/v3/features/modular-applications/

This is helpful if you want to group your routes into a domain or component of a larger application.

There have been a few suggestions about how to do this in Slim:

In my opinion these feel somewhat disorganized and error prone. This also doesn’t address problems with dependency injection (i.e. all your DI configurations need to still be declared together).

Does the team plan to add any interface support for this functionality? Ideally users would be able to specify a route “prefix” all declared routes would include and the dependencies the given route grouping needs.

Hello @gdanis

First, I am not part of the core, so I cannot speak for the Slim Framework core members, but only from my personal perspective.

The organization of projects is a very specific task, because every project (and company) is different. Slim does not force a fixed structure.

http://www.slimframework.com/2011/09/24/how-to-organize-a-large-slim-framework-application.html

IMHO this link quite outdated. I would recommend the pds/skeleton for the project root directories.

In my opinion they feel a bit disorganized and error-prone.

Multiple routing files

In my experience, this is not a good approach when it comes to testability, single responsibility (SOLID), static analysis, refactoring, etc… Instead, I would organize the “controller” in a directory and the “action” in a single action class.

Example:

src/
   Action/
       User/
           CreateUserAction.php
           UpdateUserAction.php
           DeleteUserAction.php
       Invoice/
           CreateInvoiceAction.php
           PrintInvoiceAction.php
           ...

Organizing the “Domain” layer depends on your needs.

This also does not fix any dependency injection problems (i.e. all your DI configurations still need to be declared together).

Use autowiring to keep your container entries minimal. I am maintaining a project with over 1000+ classes and my container contains only 10 entries because I use the autowrite feature and (constructor) dependency injection.

The central declaration of dependencies is a big plus in terms of maintenance and debugging, it is also faster. But again, if you use Dependency Injection and Autowiring, you only need tiny set of container entries, even in large projects.

Does the team plan to add interface support for this functionality?

Slim itself is not responsible for the container implementation. But you can already manually bind each interface to a class. No container can do this for you because the container cannot know which class you want to inject.

1 Like

First of all, I’m new to php and web development.

I’m also looking for a separate by “modules” structure.
While I think that slim should not enforce that by reason that @odan said, I feel there is a lack of resources on this theme.

I was experimenting with a repeated basic structure inside each module in a “modules” directory, while keeping generic and shared functionality in a “core” directory.

src/
|__core/
   |___Action/
   |___Domain/
   |___Routes/
       |_____api.php
       |_____web.php
   |___templates/
   |___...
|__modules/
|_________ModuleA/
          |______Action/
          |______Domain/
          |______Routes/
                 |_____api.php
                 |_____web.php
          |______templates/
          |______...
|_________ModuleB/

To load routes (and templates) for each module a assumed a given pattern, that is, the main “route.php” load a “group route” (named by the module nome) for each module based on the directory structure: modules//Routes/<api.php|web.php>

I also separated “api” and “web” routes inside Routes dir.
For each module then I could share functionality to either api or web routes.

Of course any deviations from the imposed structure would break routes loading. Instead, its possible to write a configuration for that, which would also makes easy to enable/disabled the module.

@odan what are yout thoughts on this approach? Do you have any concerns why that would not be good for long-term, possible growing bigger project?

PS:
Here are some thoughts about project structure:

I tried something with a similar approach and file structure, it works well enough and i would prefer to handle my own modules anyway, its not something i would use even if the framework offered it

so far ive only had a chance to make a basic test so its kinda crude but might give you some ideas
at the end of the main index file i used this ugly business to kick it off for now

foreach (glob(’…/app/module/ * / * .Config.php’) as $file){ include($file); }
(ignore extra spaces not sure why it wont display slash star slash star)

ie a path like this

“/app/module/test/Test.Config.php”

which is a class that instantiates itself, which can dial back to the main application to register and request further loading

i just use the module folder / key as a url group to keep the urls separate

then i just relative my way to the template dir in the controller for now

$this->renderStandard($response, ‘/…/…/module/test/view/page/getTest.php’, $args);

which at least got me into a template and on screen

so rather than trying to do bootstrap/container/route for core and all modules at the same time, i just do bootstrap/container/route for core first, then load each module completely one at a time, where more checking / communication with core can be done and then hopefully a module shouldnt be able to sink the main app and it should be possible to install/uninstall/enable/disable/rollback whatever

only unknown i have is the public resources

im still rendering with the main application layouts so i have the core js/css loaded
i could do the same again and expand on something like this in the template with the head tag

‘…/app/module/public/*.js’) as $file) etc

but i dont really want to go rifling through modules from the core aside from the main loading
i want all the installing/loading code in one spot so it either works or doesnt and cant get stuck half way

i could use rendered sub-templates with script tags in templates to reuse css/js but that doesnt help me with images icons downloads etc

which i think leaves me at putting a module directory in the main public folder or some kind of mod_rewrite hackery

Hi @raffster

I use a very similar aproach like Nikola Poša has written down in his blog post.

  • The project root directories complies with the php-pds/skeleton.
  • I put my “modules”, the core of my application, into the src/Domain directory.
  • Then I structure the src/Domain sub-directories by “module”. e.g. src/Domain/Customer
  • Per module I create sub-directories per “type”. For example I put all repositories of the “Customer” module into a specific repository directory. For example: src/Domain/Customer/Repository/CustomerListRepository.php.
  • For the business logic (validation, calculation, file creation, etc…) I create specific “Service” classes for each use case. A service class should have only one public method.

More details about how to structure the src/Domain sub-directories: https://blog.juliobiason.net/thoughts/things-i-learnt-the-hard-way/#organize-projects-by-data-type-not-functionality

Example: https://github.com/odan/slim4-skeleton/tree/master/src/Domain/User