# How to Apply Laravel Modular Approach Architecture in 9 Easy Steps

Laravel is powerful and very intelligent PHP Framework that can serve you from a very simple web application to large and enterprise level applications, Well that’s a good news but in other hand using such a large framework without any architecture approach in mind can lead to a massive mess and downfalls in the process of expanding and in production environment.

While Laravel has it’s own design system we can’t only rely on this. Imagine an Application with tons of Features that heavily depends on Each others, in this case maintaining any little refactoring or improvements can cause much problems and bugs. That’s we need to start with a modular architecture that helps separate and maintain each feature as a standalone package.

# What is Modular Architecture

Modular Architecture is a way of your Laravel Project where each Feature is considered a standalone Block that can nicely work with other Blocks or Features. Its also serves the concept of Separation of Concerns so when you develop a Feature you have to only focus on that Feature and its Logic and how it can be used by any other Feature. So each Feature is a Module, for example we have Modules like :

```bash
Users, Blog, Pages, Products, .... etc
```

# The Big Picture

In order to Apply the Modular Architecture we will need to separate each module in a dedicated directory which will contain the module’s Controller, Migrations, Views, Routes, Models, Providers and each piece of code that’s related to this Module. So we will need to instruct Laravel how to load each component of each module correctly.

To Achieve this separation we will rely on [Service Providers](https://laravel.com/docs/9.x/providers), where each module will contact a Module Service Provider that will instruct Laravel how to interact with and Load that Module.

# Create our Project

Lets create our laravel project which we will use to apply modular architecture

```bash
composer create-project "laravel/laravel" modular
```

Give composer few time to download and install required packages then after installation complete go to the installation directory using `cd` command and run the `serve` command to start up our application

```bash
cd modular
php artisan serve
```

Now you can visit your application in the browser using the link `http://127.0.0.1:8000` , Note : keep eye on terminal output as the port 8000 may change.

![1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667516792887/UrjHv2vS5.png align="left")

# Modules Structure

We will start by creating a directory called Modules that will contain all our modules, Each Module will by consist of multiple directories and files, so our directory tree for our Modules directory will look something like this

![2.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667516819285/LyeAigLbf.png align="left")

```bash
Modules
- Blog
-- Config
-- Database
-- Http
-- Models
-- Providers
-- View
-- Routes
+ Pages
+ Products
```

# Configure Composer

Its very important to tell composer where to find and how to load our modules files and classes and before that we have to keep in mind that we will use namespaces to differentiate modules so each module fille will be prefixed by namespace of `Modules\MODULE_NAME` so for example

```bash
<?php 
namespace Modules\Blog\Providers;

....
```

So we need to add the base namespace Modules pointing to the Modules directory in composer.json file under autoload &gt; psr-4 as follows

![3.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667516885525/YIKPJIuw0.png align="left")

don’t forget to run `composer dump-autoload` command

# Module Service Provider

Now Composer can find our Modules classes, so it’s the time to create the module Service Provider that will instruct laravel how to interact with our module, we will apply this to our Blog module as demonstration. Go to `Modules > Blog > Providers` and create a new class file `ModuleServiceProvider.php`, with following contents

```bash
<?php

namespace Modules\Blog\Providers;

use Illuminate\Support\ServiceProvider;

class ModuleServiceProvider extends ServiceProvider
{
    public function register()
    {
        //...
    }

    public function boot()
    {
        //...
    }
}
```

# Load Modules Providers

As we have seen before, each module should has it’s own Service Provider that we will use to instruct laravel how to load and interact with out module, but how laravel will know about this Service Provider ?

We will need to tell Laravel to Scan the Modules Directory to find installed modules and for each module we will look for the `ModuleServiceProvider` class and load it in the System. Best place to do this is in `AppServiceProvider.php` located in `app/Providers` Directory. withint the Register method as follows :

```bash
<?php

namespace App\Providers;

use Illuminate\Support\Facades\File;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    //....

    public function register()
    {
        //Search for Modules
        $modulesBasePath = base_path('Modules');
        foreach(File::directories($modulesBasePath) as $moduleDirectory)
        {
            //Get Module Name from the owner Directory name
            $moduleName = basename($moduleDirectory);
            $providerClassName = '\\Modules\\'.$moduleName.'\\Providers\\ModuleServiceProvider';
            if(class_exists($providerClassName))
            {
                //Register Module Service Provider if Exist
                $this->app->register($providerClassName);
            }
        }
    }

    //....
}
```

Now Laravel will be aware of every module Service Provider, so next steps will be all included inside that providers

# Routes Discovery

As each module will contain its own routes then we will create a new directory into the module named `Routes` which will contain a `web.php` file, that file where we will define the module routes, and as a start will will add just one route to test if it’s working

```bash
<?php

use Illuminate\Support\Facades\Route;

Route::get('/blog', function(){
    return 'Yay, Blog Routes Working';
});
```

If we tried to visit that route, it will give us 404 Not Found error as Laravel not yet aware of that file, so we will add a `RouteServiceProvider.php` that will load our `web.php` routes file, in the directory of `Modules/Blog/Providers`

```bash
<?php

namespace Modules\Blog\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{

    public function map()
    {
        $this->registerWebRoutes();
    }

    protected function registerWebRoutes()
    {
        //Add Web Routes with web Guard
        Route::middleware('web')
            //Set Default Controllers Namespace
            ->namespace('Modules\\Blog\\Http\\Controllers')
            ->group(base_path('Modules/Blog/Routes/web.php'));
    }
}
```

Note that we are now extending the `Illuminate\Foundation\Support\Providers\RouteServiceProvider` not the regular Service provider, this will tell laravel to call the map function automatically

Before we test we need to tell Laravel to also load that `RouteServiceProvider` inside the register method of the `ModuleServiceProvider`

```bash
<?php

namespace Modules\Blog\Providers;

use Illuminate\Support\ServiceProvider;

class ModuleServiceProvider extends ServiceProvider
{
    public function register()
    {
		//...
        $this->app->register(RouteServiceProvider::class);
    }

    //...
}
```

Now if we visit the test route at `http://127.0.0.1:8000/blog` it should work.

![4.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667517106991/uFae07cVa.png align="left")

# Module Views

Until now we can create routes but we only return a String, what if we want to return a Blade view, and how to differentiate views for each module using views namespacing.

It’s very easy, all we need to do is to register our custom Views path to Laravel views sources inside the `ModuleServiceProvider` boot method

```bash
<?php

namespace Modules\Blog\Providers;

use Illuminate\Support\ServiceProvider;

class ModuleServiceProvider extends ServiceProvider
{

    protected String $moduleName = 'Blog';

	//...

    public function boot()
    {
        $this->registerViews();
        //...
    }

    protected function registerViews()
    {
        $moduleViewsPath = __DIR__.'/../Views';
        $this->loadViewsFrom($moduleViewsPath, strtolower($this->moduleName));
    }
}
```

Lets test that by creating a new view inside Views directory and return it from our test route.

```bash
<?php

use Illuminate\Support\Facades\Route;

Route::get('/blog', function(){
    return view('blog::index');
});
```

```bash
<h1>Blog Module</h1>
<p>This view is loaded directly from Blog Module /Views directory</p>
```

Continue using Controllers

We can take that a step further and create our first modular controller for our module at the path of `Modules\Blog\Http\Controllers` named `BlogController`

```bash
<?php

namespace Modules\Blog\Http\Controllers;

use Illuminate\Routing\Controller;

class BlogController extends Controller
{
    public function index()
    {
        return view('blog::index');
    }
}
```

```bash
<?php

use Illuminate\Support\Facades\Route;
use Modules\Blog\Http\Controllers\BlogController;

Route::get('/blog', [BlogController::class, 'index']);
```

![5.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667517212080/gkCuXE5IL.png align="left")

# Register Migrations

Each module may contain its own database migration, that why we need to make laravel aware of this migrations which will once again be done inside `ModuleServiceProvider` `boot` method

```bash
?php

namespace Modules\Blog\Providers;

use Illuminate\Support\ServiceProvider;

class ModuleServiceProvider extends ServiceProvider
{
	//...
    public function boot()
    {
        $this->registerViews();
        $this->registerMigrations();
        //...
    }
	//...    
    protected function registerMigrations()
    {
        $this->loadMigrationsFrom(base_path('Modules/'.$this->moduleName.'/Database/Migrations'));
    }
	//...
}
```

Lets create a new Migration at path `/Modules/Blog/Database/Migrations` , which we will name as `create_posts_table.php`

```bash
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {

    public function up()
    {
        Schema::create('posts', function(Blueprint $table){
            $table->string('title');
            $table->text('content')->nullable();
            $table->unsignedBigInteger('author_id')->default(0);
            $table->unsignedBigInteger('category_id')->default(0);
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('posts');
    }

};
```

Now let’s run the migration command to make sure it works

```bash
php artisan migrate
```

![6.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667517299533/jbVlxj0De.png align="left")

# Merge Configurations

Since your modules may contains a configuration files, then we need to tell laravel to merge this files into main configurations to be able to retrieve it with `config()` function, this is once again can be done in the `ModuleServiceProvider` as follows

```bash
<?php

namespace Modules\Blog\Providers;

use Illuminate\Support\ServiceProvider;

class ModuleServiceProvider extends ServiceProvider
{
	//...
    public function boot()
    {
        $this->registerConfig();
        //...
    }
   	//...
    protected function registerConfig()
    {
        $this->mergeConfigFrom(
            base_path('Modules/'.$this->moduleName.'/Config/config.php'), strtolower($this->moduleName)
        );
    }
}
```

Now we can reference our config file using `config()` function by prefixing with the lower-case module name for example

```bash
config('blog.post_per_page', 10);
```

# Models, Middlewares and Events, What about ?

Those type of files is just regular classes like controllers, so laravel don’t need any extra work to be aware of as long as you use the right classes namespacing and directory structure.

#Final ModuleServiceProvider

```bash
<?php

namespace Modules\Blog\Providers;

use Illuminate\Support\ServiceProvider;

class ModuleServiceProvider extends ServiceProvider
{

    protected String $moduleName = 'Blog';

    public function register()
    {
        $this->app->register(RouteServiceProvider::class);
    }

    public function boot()
    {
        $this->registerConfig();
        $this->registerViews();
        $this->registerMigrations();
    }

    protected function registerViews()
    {
        $moduleViewsPath = __DIR__.'/../Views';
        $this->loadViewsFrom($moduleViewsPath, strtolower($this->moduleName));
    }

    protected function registerConfig()
    {
        $this->mergeConfigFrom(
            base_path('Modules/'.$this->moduleName.'/Config/config.php'), strtolower($this->moduleName)
        );
    }

    protected function registerMigrations()
    {
        $this->loadMigrationsFrom(base_path('Modules/'.$this->moduleName.'/Database/Migrations'));
    }
}
```

# Final Words

Good Applications starts with a good structure and design pattern so it’s always the best to spend sometime designing your application structure.

I would also like to mention that our new Laravel Package has been release which will help developers to add Actions and Filters feature in Laravel Application like WordPress Apps, You can check out full tutorial of Eventual Events Package Here

Also i’m waiting for your questions and requests if you have any.
