Aller au contenu

Domaines DDD — Guide d'implémentation

Ce document explique la structure concrète de chaque domaine DDD dans Primatch et donne des exemples d'implémentation.


Structure type d'un domaine

app/Domain/{Feature}/
├── DTOs/                    # Immutable data carriers
│   ├── Create{Feature}DTO.php
│   └── Update{Feature}DTO.php
├── Events/                  # Domain events
│   └── {Feature}Created.php
├── Models/                  # Business entities / Value Objects
│   └── {Feature}Status.php  # (Enum)
├── Repositories/            # Interfaces only
│   └── {Feature}RepositoryInterface.php
└── Services/                # Business logic
    └── {Feature}Service.php

DTOs (Data Transfer Objects)

Les DTOs sont des objets immuables qui transportent les données entre les couches.

<?php

namespace App\Domain\Game\DTOs;

use Carbon\Carbon;

final readonly class CreateGameDTO
{
    public function __construct(
        public readonly string $type,         // 'friendly' | 'competitive'
        public readonly Carbon $scheduledAt,
        public readonly int    $creatorId,
    ) {}

    public static function fromRequest(CreateGameRequest $request): self
    {
        return new self(
            type:        $request->validated('type'),
            scheduledAt: Carbon::parse($request->validated('scheduled_at')),
            creatorId:   auth()->id(),
        );
    }
}

Value Objects

Les Value Objects représentent des concepts métier immuables et identifiés par leur valeur.

<?php

namespace App\Domain\Game\Models;

final readonly class Score
{
    public function __construct(
        public readonly int $homeGamesSet1,
        public readonly int $awayGamesSet1,
        // ...
    ) {
        $this->validate();
    }

    private function validate(): void
    {
        // Implémente les règles RB-MATCH-020
        if (!$this->isValidPadelSet($this->homeGamesSet1, $this->awayGamesSet1)) {
            throw new \InvalidArgumentException('Score de set invalide selon les règles padel');
        }
    }

    private function isValidPadelSet(int $home, int $away): bool
    {
        // 6-x avec 2 de différence, ou 7-5, ou 7-6
        return /* logique de validation */ true;
    }
}

Domain Events

<?php

namespace App\Domain\Game\Events;

use App\Models\Game;
use Illuminate\Foundation\Events\Dispatchable;

final class GameCompleted
{
    use Dispatchable;

    public function __construct(
        public readonly Game $game,
    ) {}
}

Les listeners (dans app/Listeners/) réagissent aux events : - GameCompletedSendGameSummaryNotification - UserRegisteredSendWelcomeEmail