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 : - GameCompleted → SendGameSummaryNotification - UserRegistered → SendWelcomeEmail