Today I'll be talking about this topic that seems to be pretty hot lately in Laravel community. There are countless threads on Laracasts and reddit about this. What's that? Modules. Especially how to make your Laravel applications modular.

Modules

Think of modules as sections of your application that operate on their own. For example let's say you build an app where you organize events and sell tickets. Users can come and browse their local events, buy tickets, view their bought tickets, etc. Event organizers can publish and manage events and view all sold tickets. And you, as a developer, have access to "admin panel" where you can view all events, all users, log files -- so you can manually update a resource if an error happens without digging into the database. Each of these sections are so-called modules: user dashboard, organizer dashboard and admin panel. This approach to development is called Domain-Driven Design. Each module contains logic and code specific to its domain. Organizer's dashboard module will contain its own routes, models and controllers that only handle the requests made on those pages.

The "how"

There are many packages that allow you to make your apps modular. Before importing a package, make sure you understand how the core works and how to make it yourself. Once you fully understand how it works, feel free to import any package to save time. Unless you're a beginner and just started using Laravel, you should know that everything except the vendor directory can (and should) be customized to your own needs. No two apps are the same, right? So why design them the same way?

For example: you don't like the name app for a directory? No problem, rename it to src, update composer.json to pick up on that, extend the Illuminate\Foundation\Application with path() method and you're done. Now you're using the src directory instead of app. The same thing is with directory structures. You should know that your Controllers don't have to be placed at app/Http/Controllers and that routes don't have to be loaded from routes/web.php file. If you examine default RouteServiceProvider that comes with default Laravel installation, you'll see this nice piece of code:

protected $namespace = 'App\Http\Controllers';

// ... rest of the code

Route::middleware('web')
     ->namespace($this->namespace)
     ->group(base_path('routes/web.php'));

Yeah, you can load your controllers and routes from wherever you want. This is essentially what these packages do. I don't really like using third party code unless I really have to, so let me explain how you can make your application modular in Laravel with no package.

Implementation

There are many ways to structure modules for your app without any third party code, and I'll describe the two I really like: Namespaces (modules based on a directory structure), and extracting a module into Composer package.

Namespacing

You should be familiar with PSR-4 and understand PHP namespaces. PSR-4 is a standard for autoloading classes. So if I have a class located in app/Blog/Controllers/PostsController, it's namespace should be: App\Blog\Controllers. If it is, the class will be autoloaded and can be used. We can use that to structure our directories the way we want. We can put routes related to a Blog module in app/Blog/routes.php, controllers into app/Blog/Controllers/ and any additional classes the same way. Yeah, even your Form Requests don't have to be located in app/Http/Requests. They're autoloaded, so we can place them into app/Blog/Requests/, namespace properly and use them in controllers. Heck, you can even place views into app/Blog/views/, namespace the view in custom service provider with View::addNamespace('blog', app_path('Blog/views')); and use them like: return view('blog::posts.index');. If you want to get really crazy, you can extend the core Application class and register a path for Blog module by adding a blogPath($path) method, so all the files related to a Blog will be located at app()->blogPath(). Don't get me wrong, ALL the code that's responsible for Blog can go into the Blog directory, not only controllers and routes.

I mentioned extending Application class, if you do that, you should tell Laravel about your Application class by replacing Illuminate\Foundation\Application with your own in bootstrap/app.php. This is a bit advanced, so do it if you know what you're doing.

Register the module

After all the code has been migrated to Blog directory, Laravel doesn't know about the module you just built. It doesn't know it should read routes from your own custom file, or register the Controllers. At this point, you should be familiar with Service Providers. I've talked about them in my Laravel Behind the Scenes: Lifecycle series so give it a look if you haven't. Service Provider is essentially a class that hooks into Laravel core and sets up any additional stuff it needs to. In our case, it needs to load and register routes. No problem, as we've seen before, RouteServiceProvider does the same thing. So we can copy RouteServiceProvider class into the app/Blog/Providers/ directory and make the changes it needs:

Route::middleware('web')
     ->namespace('App\Blog\Controllers')
     ->group(app_path('Blog/routes.php'));

You can do the same if you need any custom config files in Blog directory. Note that all classes are autoloaded, so your Blog's controllers can use any models located in your directory, just make sure to import them. At this point, Laravel still doesn't know about the module. That's because we should add the provider to the config/app.php file at the providers array. Add your App\Blog\Providers\RouteServiceProvider somewhere in the array, refresh the page and voila. You've got yourself an app where all the code related to Blog is located in the app/Blog/ directory. Do this for all your modules and you can safely remove routes/web.php and app/Http/Controllers/. You can move some core logic such as HTTP/Console kernel, AppServiceProvider, AuthServiceProvier or whatever is left into a directory, for example app/Core. Just make sure to catch up where these classes are used and replace them with correct namespace.

Composer packages

The other way people like to structure your app is by extracting modules into a Composer package and require them. Like so: composer require my-cool-app/blog. That way, your Laravel installation will remain as clean as possible. I don't really recommend or use it, but you can do it.

Summary

The approach I described here looks like it takes a lot of time to structure, but trust me, after you've done this couple times, you'll understand how it works and you'll be splitting your apps into modules within minutes and most importantly, you will have a full control over the code and understand how it works. And basically, once you create first module, you can just copy the entire module into another folder, remove the logic code (controllers, routes) and you've got yourself another module.

You should realize that Laravel is REALLY customizable, you don't have to stick to default directory structure and files it comes with. For example, I really like to keep core models of my application within the app/ directory. Meaning User model, Post model, Comment model will be kept within app/ directory, but all the other models such as Vote, Like, Bookmark, UserSettings (entities that are not core of my app) go into separate folders, maybe app/Support or something related to their domain. I've also been mixing Single-Action controllers with CRUD (default) Controllers for years and it works great for me (let me know if you want me to talk about it). These are just an examples to help you realize you don't have to stick to one design and go with it, you can completely tailor each application to your own needs.

If you have any questions or something is not clear to you, I'd be more than happy to go into more details, just hit me up on Twitter, email or comments.