210 lines
6.1 KiB
Plaintext
210 lines
6.1 KiB
Plaintext
---
|
|
description:
|
|
globs: *.php,src/*
|
|
alwaysApply: false
|
|
---
|
|
# Architecture Hexagonale de Mangarr
|
|
|
|
## Structure Générale
|
|
L'application suit une architecture hexagonale (ports & adapters) avec une séparation claire des responsabilités. Le code métier est organisé en domaines distincts dans le dossier `src/Domain/`.
|
|
|
|
```
|
|
src/
|
|
└── Domain/
|
|
├── Shared/ # Code partagé entre les domaines
|
|
├── Manga/ # Domaine de gestion des mangas
|
|
├── Reader/ # Domaine de lecture
|
|
└── Scraping/ # Domaine de scraping
|
|
```
|
|
|
|
## Organisation des Domaines
|
|
Chaque domaine suit la même structure hexagonale :
|
|
|
|
```
|
|
Domain/Manga/
|
|
├── Domain/ # Cœur métier
|
|
│ ├── Entity/ # Entités du domaine
|
|
│ ├── ValueObject/ # Objets de valeur
|
|
│ ├── Event/ # Événements du domaine
|
|
│ └── Exception/ # Exceptions métier
|
|
├── Application/ # Cas d'utilisation
|
|
│ ├── Command/ # Commandes (DTO)
|
|
│ ├── CommandHandler/# Gestionnaires de commandes
|
|
│ ├── Query/ # Requêtes (DTO)
|
|
│ ├── QueryHandler/ # Gestionnaires de requêtes
|
|
│ └── Response/ # Objets de réponse (DTO)
|
|
└── Infrastructure/ # Adaptateurs
|
|
├── Repository/ # Implémentation des repositories
|
|
├── Service/ # Services techniques
|
|
└── Persistence/ # Persistence des données
|
|
```
|
|
|
|
## Règles d'Architecture
|
|
|
|
### 1. Règles Générales
|
|
- Tout le code métier doit résider dans le namespace `App\Domain`
|
|
- Les dépendances externes doivent être limitées et explicitement autorisées
|
|
- Les exceptions standards et utilitaires autorisés :
|
|
- `DateTimeImmutable`
|
|
- `RuntimeException`
|
|
- `Exception`
|
|
- `DomainException`
|
|
- `Symfony\Component\HttpKernel\Exception`
|
|
- `InvalidArgumentException`
|
|
|
|
### 2. Domaine Shared
|
|
- Le domaine `Shared` ne doit dépendre d'aucun autre domaine
|
|
- Il contient les contrats et les types partagés entre les domaines
|
|
- Exemple : `App\Domain\Shared\Contract\UuidInterface`
|
|
|
|
### 3. Couche Domain
|
|
- Ne doit dépendre que d'elle-même et du domaine Shared
|
|
- Contient la logique métier pure
|
|
- Ne doit pas avoir de dépendances externes
|
|
- Structure des composants :
|
|
- Les `Entity` sont les objets métier principaux
|
|
- Les `ValueObject` sont immuables et s'auto-valident
|
|
- Les `Event` représentent les changements d'état du domaine
|
|
- Les `Exception` définissent les erreurs métier spécifiques
|
|
|
|
### 4. Couche Application
|
|
- Peut dépendre de son propre domaine et du domaine Shared
|
|
- Peut utiliser les dépendances externes autorisées :
|
|
- `Symfony\Component\Messenger`
|
|
- `Ramsey\Uuid`
|
|
- Ne doit JAMAIS dépendre de la couche Infrastructure
|
|
- Structure des composants :
|
|
- Les `Query` sont des DTO (Data Transfer Objects) en lecture seule
|
|
- Les `Command` sont des DTO pour les modifications
|
|
- Les `QueryHandler` doivent :
|
|
- Implémenter `QueryHandlerInterface`
|
|
- Prendre une seule `Query` en paramètre
|
|
- Retourner une `Response`
|
|
- Les `CommandHandler` doivent :
|
|
- Implémenter `CommandHandlerInterface`
|
|
- Prendre une seule `Command` en paramètre
|
|
- Ne pas retourner de valeur (void)
|
|
- Les `Response` sont des DTO immuables pour les résultats de requêtes
|
|
|
|
### 5. Couche Infrastructure
|
|
- Implémente les interfaces définies dans le domaine
|
|
- Peut dépendre de toutes les couches de son domaine
|
|
- Contient les adaptateurs pour les services externes
|
|
- Structure des composants :
|
|
- Les `Repository` implémentent les interfaces du domaine
|
|
- Les `Service` fournissent des fonctionnalités techniques
|
|
- La `Persistence` gère le stockage des données
|
|
|
|
## Flux de Dépendances
|
|
```
|
|
Infrastructure → Application → Domain
|
|
↓ ↓ ↓
|
|
External Shared Shared
|
|
```
|
|
|
|
## Validation
|
|
Les règles d'architecture sont validées par phparkitect. Les violations de ces règles entraîneront une erreur lors de la validation.
|
|
|
|
## Exemples de Code
|
|
|
|
### Domain Layer
|
|
```php
|
|
namespace App\Domain\Manga\Domain\Entity;
|
|
|
|
class Manga
|
|
{
|
|
private MangaId $id;
|
|
private Title $title;
|
|
private Description $description;
|
|
|
|
public function __construct(MangaId $id, Title $title)
|
|
{
|
|
$this->id = $id;
|
|
$this->title = $title;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Application Layer
|
|
```php
|
|
namespace App\Domain\Manga\Application\Query;
|
|
|
|
readonly class GetMangaByIdQuery
|
|
{
|
|
public function __construct(
|
|
public string $id
|
|
) {}
|
|
}
|
|
|
|
namespace App\Domain\Manga\Application\QueryHandler;
|
|
|
|
class GetMangaByIdQueryHandler implements QueryHandlerInterface
|
|
{
|
|
public function __construct(
|
|
private MangaRepositoryInterface $mangaRepository
|
|
) {}
|
|
|
|
public function __invoke(GetMangaByIdQuery $query): MangaResponse
|
|
{
|
|
$manga = $this->mangaRepository->get($query->id);
|
|
return new MangaResponse($manga);
|
|
}
|
|
}
|
|
|
|
namespace App\Domain\Manga\Application\Command;
|
|
|
|
readonly class CreateMangaCommand
|
|
{
|
|
public function __construct(
|
|
public string $title,
|
|
public ?string $description = null,
|
|
) {}
|
|
}
|
|
|
|
namespace App\Domain\Manga\Application\CommandHandler;
|
|
|
|
class CreateMangaCommandHandler implements CommandHandlerInterface
|
|
{
|
|
public function __construct(
|
|
private MangaRepositoryInterface $mangaRepository
|
|
) {}
|
|
|
|
public function __invoke(CreateMangaCommand $command): void
|
|
{
|
|
$manga = Manga::create($command->title, $command->description);
|
|
$this->mangaRepository->save($manga);
|
|
}
|
|
}
|
|
|
|
namespace App\Domain\Manga\Application\Response;
|
|
|
|
readonly class MangaResponse
|
|
{
|
|
public function __construct(
|
|
public string $id,
|
|
public string $title,
|
|
public ?string $description
|
|
) {}
|
|
|
|
public static function fromEntity(Manga $manga): self
|
|
{
|
|
return new self(
|
|
$manga->getId()->toString(),
|
|
$manga->getTitle()->value(),
|
|
$manga->getDescription()?->value()
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Infrastructure Layer
|
|
```php
|
|
namespace App\Domain\Manga\Infrastructure\Repository;
|
|
|
|
use App\Domain\Manga\Domain\Repository\MangaRepositoryInterface;
|
|
|
|
class DoctrineMangaRepository implements MangaRepositoryInterface
|
|
{
|
|
// Implementation
|
|
}
|
|
``` |