<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
creativecrafts / laravel-domain-driven-design-lite example snippets
namespace Modules\Planner\Domain\Actions\Trip;
use Modules\Planner\Domain\Contracts\TripRepositoryContract;
use Modules\Planner\Domain\DTO\CreateTripData;
final class CreateTripAction
{
public function __construct(
private TripRepositoryContract $repo,
) {}
public function __invoke(CreateTripData $data): string
{
// Domain invariants live here
return $this->repo->create($data);
}
}
namespace Modules\Billing\Domain\Actions;
use Modules\Billing\Domain\Contracts\InvoiceRepositoryContract;
use Modules\Billing\Domain\DTO\CreateInvoiceData;
final class CreateInvoiceAction
{
public function __construct(private InvoiceRepositoryContract $repository)
{
}
public function execute(CreateInvoiceData $data): string
{
return $this->repository->create($data);
}
}
namespace Modules\Billing\App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
final class StoreInvoiceRequest extends FormRequest
{
public function rules(): array
{
return [
'title' => ['
namespace Modules\Billing\App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Modules\Billing\App\Http\Requests\StoreInvoiceRequest;
use Modules\Billing\Domain\Actions\CreateInvoiceAction;
use Modules\Billing\Domain\DTO\CreateInvoiceData;
final class InvoiceController
{
public function __construct(private CreateInvoiceAction $action)
{
}
public function store(StoreInvoiceRequest $request): JsonResponse
{
$data = new CreateInvoiceData(
$request->string('title')->toString(),
$request->integer('amount'),
);
$id = $this->action->execute($data);
return response()->json(['id' => $id], 201);
}
}
use Modules\Billing\Domain\Actions\CreateInvoiceAction;
it('orchestrates the domain action from the controller', function (): void {
$action = Mockery::mock(CreateInvoiceAction::class);
$action->shouldReceive('execute')->once()->andReturn('inv_123');
$this->instance(CreateInvoiceAction::class, $action);
$response = $this->postJson('/billing/invoices', [
'title' => 'Pro Plan',
'amount' => 1200,
]);
$response->assertCreated()->assertJson(['id' => 'inv_123']);
});
namespace Modules\Payments\Domain\DTO;
final class CreatePaymentData
{
public function __construct(
public string $reference,
public int $amount,
) {
}
}
namespace Modules\Payments\Domain\Contracts;
use Modules\Payments\Domain\DTO\CreatePaymentData;
interface PaymentRepositoryContract
{
public function create(CreatePaymentData $data): string;
}
namespace Modules\Payments\App\Repositories;
use Modules\Payments\App\Models\Payment;
use Modules\Payments\Domain\Contracts\PaymentRepositoryContract;
use Modules\Payments\Domain\DTO\CreatePaymentData;
final class PaymentRepository implements PaymentRepositoryContract
{
public function create(CreatePaymentData $data): string
{
$payment = Payment::query()->create([
'reference' => $data->reference,
'amount' => $data->amount,
]);
return (string) $payment->getKey();
}
}
namespace Modules\Payments\Domain\Actions;
use Modules\Payments\Domain\Contracts\PaymentRepositoryContract;
use Modules\Payments\Domain\DTO\CreatePaymentData;
final class CreatePaymentAction
{
public function __construct(private PaymentRepositoryContract $repository)
{
}
public function execute(CreatePaymentData $data): string
{
return $this->repository->create($data);
}
}
namespace Modules\Shared\Domain\ValueObjects;
final class TenantId
{
public function __construct(public string $value)
{
}
}
namespace Modules\Tenancy\Domain\Contracts;
use Modules\Shared\Domain\ValueObjects\TenantId;
interface CurrentTenantResolverContract
{
public function resolve(): TenantId;
}
namespace Modules\Projects\App\Actions;
use Modules\Shared\Domain\ValueObjects\TenantId;
use Modules\Tenancy\Domain\Contracts\CurrentTenantResolverContract;
final class BuildTenantContext
{
public function __construct(private CurrentTenantResolverContract $resolver)
{
}
public function handle(): TenantId
{
return $this->resolver->resolve();
}
}
namespace Modules\Orders\App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Modules\Orders\App\Http\Requests\StoreOrderRequest;
use Modules\Orders\Domain\Actions\CreateOrderAction;
use Modules\Orders\Domain\DTO\CreateOrderData;
final class OrderController
{
public function __construct(private CreateOrderAction $action)
{
}
public function store(StoreOrderRequest $request): JsonResponse
{
$data = new CreateOrderData(
$request->integer('customer_id'),
$request->integer('total'),
);
$id = $this->action->execute($data);
return response()->json(['id' => $id], 201);
}
}
namespace Modules\Orders\Domain\Actions;
use Modules\Orders\Domain\Contracts\OrderRepositoryContract;
use Modules\Orders\Domain\DTO\CreateOrderData;
final class CreateOrderAction
{
public function __construct(private OrderRepositoryContract $repository)
{
}
public function execute(CreateOrderData $data): string
{
return $this->repository->create($data);
}
}
declare(strict_types=1);
namespace Modules\Users\Domain\ValueObjects;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use InvalidArgumentException;
final readonly class Email
{
public function __construct(private string $value)
{
$validator = new EmailValidator();
if (!$validator->isValid($value, new RFCValidation())) {
throw new InvalidArgumentException('Invalid email address.');
}
}
public function value(): string
{
return $this->value;
}
public function __toString(): string
{
return $this->value;
}
}
use Modules\Users\Domain\ValueObjects\Email;
$email = new Email($data->email);
declare(strict_types=1);
use Modules\Users\Domain\ValueObjects\Email;
it('accepts valid email', function (): void {
$email = new Email('[email protected]');
expect((string)$email)->toBe('[email protected]');
});
it('rejects invalid email', function (): void {
new Email('not-an-email');
})->throws(InvalidArgumentException::class);
bash
# In your app
vendor/bin/phpstan analyse -c phpstan.app.neon
vendor/bin/deptrac --config=deptrac.app.yaml
php artisan test tests/ArchitectureTest.php
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.