Learn Laravel Domain Driven Development (DDD) with Task Management App using Inertia Js and Tailwind css

Root User

May 23, 2020

If you are thinking about upgrading your expertise in Laravel, then Domain Driven Development architecture would be a pillar to enhance your expertise. Domain Driven Development is a architecture pattern is in which, we separate our codes based on business domain. In this article I will further add Application layer, which will responsible for application layer logics.

All the code is available in github and site is live at task.esikai.com

Application Layer

Application layer is the layer whose main responsibility is to handle HTTP Requests (It should not have any kind of business Logics). If we consider a standard architecture, then application layer should have it's own routing, controllers, request validation (FormRequest in Laravel), Proper Response class. etc. To demonstrate this knowledge Let's create a new laravel Projects for task management.

laravel new tasks	

At first Let's defines what are that part of our application layers. As we already discussed, routing is also part of application layer, But we prefer to put routes data in the laravel default folder but in different files. Think application layer as how does your application will use by users. If we consider this tasks management app, all of the users will use our app from our frontend. There will no require of backend for this application, so all of the routes files will be defined in the routes/front.php file. If our application grows and require admin related functions then we will define routes in routes/admin.php files. If our application further expands and require customers related functions as well we will add these routes in routes/customers.php

Okay, we will defines routes in separate files, but Does these route files load automatically ?

The answer is NO. You need to load these routes from RouteServiceProviderClass In our tasks management application, let's load routes from front.php file. To do so, go to app/Providers/RouteServiceProvider.php file and define a method called mapFrontRoutes as

protected function mapFrontRoutes()
{
    Route::middleware('web')
        ->group(base_path('routes/front.php'));
}

Then this mapFrontRoutes, method is called inside from map method as

/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
    // Other route loading methods
    $this->mapFrontRoutes(); 
}

Note: that I have removed that default namespace functions, because we will load controllers from different directory.

We will define following routes in routes/front.php

<?php

use App\Application\Front\Controllers\PageController;
use App\Application\Front\Controllers\PriorityController;
use App\Application\Front\Controllers\TaskController;
use Illuminate\Support\Facades\Route;

Route::get('/', [PageController::class, 'home']);
Route::resource('/tasks', TaskController::class)->except(['index', 'show']);
Route::post('/priority', [PriorityController::class, 'store']);

Controllers

Controller is also a part of application layer, which we will define to a domain of application layers. Here all of controllers are associated to front related routing so we difine all the controllers in app\Application\Front\Controllers folder. If we consider admin related tasks then controller directory will be app\Application\Admin\Controllers Or In General the controller directory will be app\Application{Domain}\Controllers Example of a Controller that handles front requests

<?php

namespace App\Application\Front\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Domain\Project\Models\Task;
use Inertia\Inertia;

class PageController extends Controller
{
    protected $repository;

    public function __construct(Task $task)
    {
        $this->repository = $task;
    }

    /**
     * Load Home page
     *
     * @return void
     */
    public function home()
    {
        $tasks = $this->repository->orderBy('priority')->get();
        return Inertia::render('Front/Tasks/Index', ['tasks' => $tasks]);
    }
}

Request Validation

Request validation is also a part of application layers, all the laravel request validation class is written in

app\Application{Domain}\Requests. I have a TaskCreateRequest Class to validate the user input which is written as

<?php

namespace App\Application\Front\Requests;

use Illuminate\Foundation\Http\FormRequest;

class TaskCreateRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|min:3',
        ];
    }
}

Futhermore, you can have laravel Custom Rule class, Reponse Class, Api resource class in Application Layers wich is associated with Domain.

Domain

The difference between Application and Domain layer is Application Layer should only deal with with HTTP Request and Domain Layers should deals with your core business Logics. It's directly works with models and database layers. If we talk about laravel application, Domain layer must include Models, Action, Jobs, Mail, Notifications Any Interfaces, or any implementation class related to that Domain.

![Snapshot of Domain Layer one my codebase ](/home/ellite/Pictures/Screenshot from 2020-05-23 14-35-59.png)

To further explain about the how to write code in action rather than in controller I have shown a example here.

In our tasks management application, we can write our store method as

/**
 * Store Task Resources
 *
 * @param Request $request
 * @return void
 */
public function store(TaskCreateRequest $request, TasksCreateAction $tasksCreateAction)
{
    $tasksCreateAction->execute($request->all());
    return response()->json(['message' => 'Task has been created.']);
}

And TaskCreateAction class

<?php

namespace App\Domain\Project\Action;

use App\Domain\Project\Models\Task;

class TasksCreateAction
{
    public function execute($data=[])
    {
        Task::create($data);
    }
}


Benefits of this methods, is that all of your logics is moved into Action class, which is easily testable. Here is an example of testing this execute methods without hitting the controllers.

<?php

namespace Tests\Unit\Project;

use App\Domain\Project\Action\TasksCreateAction;
use App\Domain\Project\Models\Project;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TestTaskCreateAction extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function tasks_can_create()
    {
        // Prepare Data
        $data = [
            "name" => "Laborum Voluptatibu",
            "priority" => "3",
            "project_id" => factory(Project::class)->create()->id,
        ];

        // Perform Action
        app(TasksCreateAction::class)->execute($data);
        
		// Assert
        $this->assertDatabaseHas('tasks', $data);
    }
}

Conclusion

Domain Driven Development with Testing in mind great improves your code quality. It may not be the best fit for small type applications, but it is best for large size application, which has a lots of features and has huge number of domains.

All the code is available in github and site is live at task.esikai.com

Related Articles