Repositories — Pattern et implémentation¶
Le pattern Repository dans Primatch isole la logique métier de la couche de persistance. Le Domain déclare une interface (contrat), et l'Infrastructure fournit l'implémentation Eloquent.
Pourquoi ce pattern ?¶
graph LR
Service["Domain Service\n(Métier)"] -->|"dépend de"| Interface["GameRepositoryInterface\n(Contrat)"]
Interface -->|"implémenté par"| Eloquent["EloquentGameRepository\n(Infrastructure)"]
Eloquent --> DB[(PostgreSQL)] Bénéfices : - Le Domain n'a aucune dépendance sur Eloquent ou la base de données - Les tests unitaires du Domain utilisent des mocks du repository - Changer d'ORM = changer uniquement l'Infrastructure
Déclarer une interface (Domain)¶
<?php
// app/Domain/Game/Repositories/GameRepositoryInterface.php
namespace App\Domain\Game\Repositories;
use App\Domain\Game\DTOs\CreateGameDTO;
use App\Models\Game;
use Illuminate\Pagination\LengthAwarePaginator;
interface GameRepositoryInterface
{
public function findById(int $id): ?Game;
public function findByUser(int $userId): LengthAwarePaginator;
public function create(CreateGameDTO $dto): Game;
public function updateStatus(int $id, string $status): Game;
}
Implémenter le repository (Infrastructure)¶
<?php
// app/Infrastructure/Repositories/EloquentGameRepository.php
namespace App\Infrastructure\Repositories;
use App\Domain\Game\DTOs\CreateGameDTO;
use App\Domain\Game\Repositories\GameRepositoryInterface;
use App\Models\Game;
final class EloquentGameRepository implements GameRepositoryInterface
{
public function findById(int $id): ?Game
{
return Game::find($id);
}
public function create(CreateGameDTO $dto): Game
{
return Game::create([
'type' => $dto->type,
'date' => $dto->scheduledAt,
// ...
]);
}
// ...
}
Lier interface et implémentation (AppServiceProvider)¶
// app/Providers/AppServiceProvider.php
use App\Domain\Game\Repositories\GameRepositoryInterface;
use App\Infrastructure\Repositories\EloquentGameRepository;
public function register(): void
{
$this->app->bind(GameRepositoryInterface::class, EloquentGameRepository::class);
}
Tests avec Mock du repository¶
// tests/Unit/Domain/Game/GameServiceTest.php
it('creates a game with correct data', function () {
$mockRepo = Mockery::mock(GameRepositoryInterface::class);
$mockRepo->shouldReceive('create')->once()->andReturn(/* ... */);
$service = new GameService($mockRepo);
$service->createGame(new CreateGameDTO(/* ... */));
});