In this article, I would like to show you an alternative way to organize your Laravel directory structure. I think the default structure is fine for the most projects. But when it comes down to larger projects I was looking for a different structure.
For those who know me, know that I can spend hours of hours to find the name of a method or in this case defining a directory structure. It's not done within 5 minutes. It took me weeks or months. Playing around and improving. Running into issues and improving it again. Let me show you what I came up with and what I think is a real alternative to the current default structure.
I wanted the app
folder to be really simple and empty. In my experience, in this folder, there should nothing be more than the kernels. One for the Console (CLI) and on for the Http. What I did is moving the two kernels at the very first level to app
and renamed them accordingly.
app/Http/Kernel.php
➔ app/HttpKernel.php
app/Console/Kernel.php
➔ app/ConsoleKernel.php
Oh and don't forget to modify bootstrap/app.php
.Point the kernels to the new paths.
Because I want to have all my custom stuff in one place and separate the framework from the domain logic I created a src
folder. To autoload all the classes it's necessary to update the composer.json
. Let's see how it works.
"autoload": {
"psr-4": {
"App\\": ["app/", "src/"]
}
Now it's possible to use the \App
namespace in the src
directory too. The autoloader checks the app
directory first and if it doesn't find there the class you would like to autoload, it looks in the src
directory. To take that effect don't forget to tell composer that.
$ composer dump-autoload
Before we start diving deeper into it, let's move all the default things over.
app/Providers
➔ src/Provider
app/Http/Middlware
➔ src/Middlware
app/Http/Controllers
➔ src/Controller
app/Exceptions
➔ src/Exception
Because we moved the defaults, it's again necessary to modify some of the references. These are for the exceptions
bootstrap/app.php
for the providers
config/app.php
and for the middlewares
app/HttpKernel.php
You might have noticed that I did not only move some folders, I also renamed them singular. I just want to have uniformity. With the default structure, I got the feeling that there is missing some consistency. Therefore I decided to go with singular all over the place.
The last couple of years I basically follow one simple rule in the src directory. If I need to extend something or create an own component I use the namespace of that class where it originally comes from. Let me give you an example.
I often create a base Eloquent class which can be extended. In that base class, I set the guarded
property to an empty array because I want to have full control and I know what I'm doing. Anyway... People often ask me where they should put now this kind of class and here is my thumb of rule.
Put it where it originally comes from!
Originally it comes from Illuminate\Database\Eloquent\Model
, so the decision is very easy now. The src directory is kind of your individual namespace and can be seen as a placeholder for <vendor>
. With that in mind, there is only one place possible and this place is src/Database/Eloquent/Model
with the appropriate namespace. The result is something like this:
<?php
namespace App\Database\Eloquent;
use Illuminate\Database\Eloquent\Model as BaseModel;
class Model extends BaseModel
{
protected $guarded = [];
}
Now it comes to the interesting part. The Domain
folder under src
. Within the domain folder, I put my domain logic. I separate it by domain objects. Domain objects can be everything related to your domain. In my case, these are objects like User, Tenant, Subscriber, etc.
And within that object folder, I place all the related stuff to that object. For now, these are
In the future, there will be more like
There are some advantages I can see there.
First of all, it's very capsulated. If you don't need one domain object anymore, just delete the folder and you're good to go. You don't have to browse several folders to find related classes. Chances are high that you forget something.
Second is, you have all in one place. There is no more the question of where to put a class. If you only have a repository or a provider it might be easy to put it earlier within a Providers
or Repositories
folder. But if you need some more custom classes I always asked myself where to put it because these kind of classes are neither Providers or Repositories. Imagine a SubscriberCounter
, SubscriberCounterInterface
, SubscriberFactory
or something like that. Where did you put it before? Now it's clear. Well quite. In any case within the src/Domain/Subscriber
folder. Let me give you some more examples where to put something:
I like this idea a lot and if you are used to using it really makes fun.
Well, for tests I only did a couple of changes.
Instead of using Feature
and Unit
the folder at a first level within tests I decided to create a Cases
directory. Why? Easy. The same reason why I created the src directory and a Domain directory within it. With that structure, I can create more directories at the level of the tests where are needed for tests but are not tests itself. Let me give you also some examples.
The Feature
folder is still a flat directory. But within the Unit
folder, I always create an exact copy of the src
directory. As you can see I created a Domain
folder and under it, I use the same structure as above. That makes it very easy to navigate and have all the classes in the same place.
For me personally, this makes a lot of sense. This makes my daily work much easier and much more efficient. What is your opinion? Let me know what you think and what your approach is. You can contact me on Twitter at @stefanbauerme.