When using an MVC framework like Laravel your /app
folder can grow very quickly and start to get messy and complex, and if you want to share code between projects you can very easily by accident duplicate code where common functionality is required.
To overcome this a different structure is required were not everything goes into App
, but rather another directory is created were sub-projects/modules or private domain-specific packages are kept.
A recommend structure would be to create a hierarchy of dependencies, were code in App
is optionally shared between package but packages try and not depend on each other unless in the same root package namespace.
- The structure forces developers to think about the DOMAIN and logically group code based on purpose.
- It's easy for a new developer to find their way because the code is logically sectioned off by name and purpose.
- You don't have multiple un-related files in the same folder EG:
app/Http/Controllers/UserRegistration.php
&app/Http/Controllers/StockLists.php
- If ever an application needs to be broken up and embrace a microservice architecture, the process is simplified because everything is already logically grouped and mostly self-contained.
- You easily find everything important related to a package by looking at its service provider.
The location folder name suggests its purpose. Some prefer the name packages
some promote the name features
. Some argue that they are two different things and you could have a packages
or features
folder.
For simplicity, we will be using the name package
or packages
.
In this structure code in /app
provides for any common code that is shared in the application, but we try and avoid putting a feature or domain-specific code in the App
.
A common situation is where the App
namespace provides for abstract base or super classes that other packages will extend from.
Location of custom package namespaces. Each directory needs to be added to composer.json
for auto-loading to work.
A package namespace should be DOMAIN or feature specific.
For example, if you have a shopping cart checkout process, could create a /packages/ShoppingCart/
package where everything related to the checkout process is kept.
Then you could have a need for code related to statements and invoicing, so you could create a /packages/Billing/
package where all billing related code is kept.
The package structure can be and is recommended to be standardized.
Example/Recommended structure for a package:
Location | Description |
---|---|
/packages/<namespace>/README.md |
Documentation page describing the namespace and sub-packages |
/packages/<namespace>/routes.php |
Package specific routes |
/packages/<namespace>/config.php |
Package specific config |
/packages/<namespace>/ServiceProvider.php |
Laravel Service provider that registers the routes and config |
/packages/<namespace>/Contracts/* |
Package specific interfaces and contracts |
/packages/<namespace>/Controllers/* |
Package controllers that act on defined routes |
/packages/<namespace>/Commands/* |
Package CLI artisan commands |
/packages/<namespace>/Jobs/* |
Package Jobs |
To make this work you need edit your composer PSR-4 namespaces.
Every folder in /packages
will be its own root level namespace. This has the benefit of not needing to edit composer every time you want a new namespace.
EG: /packages/Sales
will become the /Sales
namespace. To do this you do not provide a name but just and an empty string "": "packages/"
.
"autoload": {
"psr-4": {
"App\\": "app/",
"": "packages/"
}
},
Each package namespace is explicitly added to Composer.
The only benefit here is that you can provide different namespace root eg /src
.
"autoload": {
"psr-4": {
"App\\": "app/",
"Examples\\": "packages/examples/src",
"System\\": "packages/system",
"Sales\\": "packages/sales",
"Utils\\": "packages/utilities"
}
},
One single branded/named namespace that all packages fall under and each sub-folder is a second level to the root namespace.
This is not very different from App
and will require extra discipline to keep it clean as it can easily get messy the same way App can get messy without clear groupings because it is a single root namespace.
"autoload": {
"psr-4": {
"App\\": "app/",
"AbcWidgets\\": "packages/"
}
},
Included here is a basic GIST example package providing all the basic elements that could go into a package in a Laravel App.
Please note there are some elements left out as it was deemed not necessary to provide examples as it would be no different from what normal documentation provides or unnecessary noise. Namely Controllers, contract interface and service class.
Additionally please note that as gist does not allow for sub-folders this example is a flat package example. Normally with large packages, you would group elements into folders as shown above.