Aller au contenu

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(/* ... */));
});