Skip to content

A Laravel project with a Domain-Driven Design (DDD) structure, basic configuration, and commonly used packages pre-installed and configured, to help you start building your next big application.

Notifications You must be signed in to change notification settings

abd-wazzan/laravel-api-boilerplate

Repository files navigation

Laravel API Boilerplate

A Laravel project with a Domain-Driven Design (DDD) structure, basic configuration, and commonly used packages pre-installed and configured, to help you start building your next big application.

Requirements

  • PHP ^8.1
  • Composer ^2.2

Installation

composer create-project abd-wazzan/laravel-api-boilerplate api-app

Install dependencies

cd api-app
composer install

Setup .env file

cp .env.example .env

Generate the application key

php artisan key:generate 

Run Locally

php artisan serve

Installed Packages

General:

Development:

Features

DDD

Software development approach that tries to bring the business language and the source code as close as possible.

This structure is inspired by LARAVEL BEYOND CRUD.

Files Structure

Domain Layer Example:

src/Domain/Invoices/
├── Actions
├── QueryBuilders
├── Collections
├── Data
├── Events
├── Exceptions
├── Listeners
├── Models
├── Rules
└── States
src/Domain/Products/
├── Actions
└── .....

Application Layer Example:

The REST API application:
src/App/Api/
├── Products
    ├── Controllers
    ├── Middlewares
    ├── Requests
    ├── Queries
    ├── Filters
    └── Resources

The Console application
src/App/Console/
└── Commands

The admin HTTP application:
src/App/Admin/
├── Products
    ├── Controllers
    ├── Middlewares
    ├── Requests
    ├── Resources
    ├── Queries
    ├── Filters
    └── ViewModels

Dependency Illustration

Resources

API Response Helper

A simple trait allowing consistent API responses throughout your Laravel application.

Available methods:

Method Status
okResponse() 200
createdResponse() 201
failedResponse() 400
unauthorizedResponse() 401
forbiddenResponse() 403
notFoundResponse() 404
unprocessableResponse() 422
serverErrorResponse() 500

Usages Example:

<?php

namespace App\Http\Api\Controllers;

use App\Traits\ApiResponseHelpers;
use Illuminate\Http\JsonResponse;
use App\Http\Controller;

class ProductController extends Controller
{
    use ApiResponseHelper;

    public function index(): JsonResponse
    {
        return $this->okResponse();
    }
}

Scribe Api Tags

Additional scribe tags that match the ApiResponseHelper responses.

Available Response tags:

Tag Status
@okResponse 200
@createdResponse 201
@failedResponse 400
@unauthorizedResponse 401
@forbiddenResponse 403
@notFoundResponse 404
@unprocessableResponse 422
@serverErrorResponse 500

Other Available tag:

Tag Description
@usesPagination will add page[number] and page[size] to the query parameters

Usages Example:

<?php

namespace App\Http\Api\Controllers;

use App\Helpers\ApiController;
use App\Traits\ApiResponseHelpers;
use Illuminate\Http\JsonResponse;
use App\Http\Controller;

/**
 * Class CategoryController
 * @group Category
 */
class CategoryController extends Controller
{
    use ApiResponseHelper;

    /**
     * Get Categories
     *
     * this request is used to get all categories.
     *
     * @queryParam filter[name]
     *
     * @usesPagination
     * @failedResponse
     * @forbiddenResponse
     * @throws \Illuminate\Auth\Access\AuthorizationException
     * @throws \Throwable
     */
    public function index(): Response
    {
        return CategoryResource::collection($categories->all());
    }

}

Global Helper

Simple php file that contains you global functions, which you can find it in ./src/shared/Helpers/global.php.

Migration Structure

In order to group your migration files by their domains, you can create additional migration directories and load them in the AppServiceProvider using loadMigrationsFrom function:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->loadMigrationsFrom([
            database_path().DIRECTORY_SEPARATOR.'migrations'.DIRECTORY_SEPARATOR.'Client',
        ]);
    }
}

Polymorphic Mapping

Please read this article first to identify the problem.

In order to achieve the morph mapping, we created the MorphEnum that will contain each model morph key and then use it in Relation::morphMap function as shown in the example:

<?php

namespace Shared\Enums;

enum MorphEnum: string
{
    case USER = 'user';
}
<?php

namespace App\Providers;

use Shared\Enums\MorphEnum;
use Domain\Client\Models\User;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Relation::morphMap([
            MorphEnum::USER->value => User::class,
        ]);
    }
}

Database Seeders

We generally have two types of seeded data:

  • Initial data: the project cannot function without it. For example, countries table data, and these data usually come from datasets.
  • Fake data: for testing purposes that can fill up any table instead of manually inserting row by row, this data is usually generated by factories.

In order to prevent the fake data from being seeded in the production environment, we created a new seeder class called TestingSeeder.php which will contain all the fake data seeders and will only run in a non-production environment. The normal seeders will stay in DatabaseSeeder.php.

Shared Directory

The src/shared/ directory is used for helper, traits, enums .... that are going to be used by the application and the domain.

Feedback

I will be happy to hear your feedback! If you have any recommendation or suggestion, please send an e-mail to Mail.

About

A Laravel project with a Domain-Driven Design (DDD) structure, basic configuration, and commonly used packages pre-installed and configured, to help you start building your next big application.

Topics

Resources

Stars

Watchers

Forks