---
description: 
globs: *.php
alwaysApply: false
---
# Architecture des Jobs dans Mangarr

## Vue d'ensemble

Le système de jobs de Mangarr est conçu pour gérer les tâches asynchrones et de longue durée de manière uniforme à travers tous les domaines. Il est basé sur une architecture centralisée dans le domaine `Shared` et peut être étendu par chaque domaine spécifique.

## Structure

```
src/Domain/Shared/
├── Domain/
│   ├── Model/
│   │   ├── Job.php              # Classe abstraite de base
│   │   ├── JobStatus.php        # États possibles d'un job
│   │   └── FailedJob.php        # Représentation d'un job échoué
│   ├── Contract/
│   │   ├── JobRepositoryInterface.php
│   │   └── FailedJobRepositoryInterface.php
│   └── Exception/
│       ├── JobNotFoundException.php
│       └── JobNotRetryableException.php
└── Infrastructure/
    ├── Persistence/
    │   ├── Entity/
    │   │   ├── JobEntity.php
    │   │   └── FailedJobEntity.php
    │   └── Repository/
    │       ├── DoctrineJobRepository.php
    │       └── DoctrineFailedJobRepository.php
    └── Service/
        └── JobRetryService.php
```

## Cycle de Vie d'un Job

### États Possibles
```php
enum JobStatus: string
{
    case PENDING = 'pending';        // Job créé, en attente d'exécution
    case IN_PROGRESS = 'in_progress';// Job en cours d'exécution
    case COMPLETED = 'completed';    // Job terminé avec succès
    case FAILED = 'failed';         // Job échoué définitivement
    case CANCELLED = 'cancelled';    // Job annulé manuellement
}
```

### Transitions d'États
1. `PENDING` → `IN_PROGRESS` : Lors du démarrage du job
2. `IN_PROGRESS` → `COMPLETED` : Lorsque le job se termine avec succès
3. `IN_PROGRESS` → `FAILED` : Lorsque le job échoue et atteint le nombre maximum de tentatives
4. `IN_PROGRESS` → `PENDING` : Lorsque le job échoue mais peut être réessayé
5. Tout état → `CANCELLED` : Lorsque le job est annulé manuellement

## Création d'un Nouveau Type de Job

1. **Créer une classe de job spécifique**
```php
class MyCustomJob extends Job
{
    public function __construct(
        string $id,
        public readonly string $someData,
        public readonly array $additionalData = []
    ) {
        parent::__construct($id, 'my_custom_job');
    }
}
```

2. **Définir le Handler**
```php
class MyCustomJobHandler
{
    public function __invoke(MyCustomJob $job): void
    {
        try {
            $job->start();
            // Logique métier
            $job->complete();
        } catch (\Exception $e) {
            $job->fail($e->getMessage());
        }
    }
}
```

## Gestion des Échecs

### Retry Automatique
- Un job peut être réessayé tant que `$attempts < $maxAttempts`
- Lors d'un échec, si des tentatives sont encore possibles, le statut redevient `PENDING`
- Les informations d'échec sont conservées dans `FailedJob`

### Informations de Debug
Chaque job contient :
- `failureReason` : La raison de l'échec
- `attempts` : Nombre de tentatives effectuées
- `context` : Données contextuelles pour le debug
- `createdAt`, `startedAt`, `completedAt` : Timestamps pour le suivi

## Bonnes Pratiques

### 1. Création de Jobs
```php
$job = new MyCustomJob(
    id: Uuid::v4(),
    someData: 'data',
    additionalData: ['key' => 'value']
);
```

### 2. Gestion du Contexte
```php
$job->context['important_info'] = 'value';
$job->context['debug_data'] = $debugInfo;
```

### 3. Retry Manuel
```php
if ($failedJob->canBeRetried()) {
    $job->attempts = 0;
    $job->status = JobStatus::PENDING;
    $jobRepository->save($job);
}
```

### 4. Monitoring
- Utiliser `findByStatus()` pour surveiller les jobs par état
- Utiliser `findFailedJobs()` pour vérifier les échecs
- Consulter `FailedJob` pour les détails des échecs

## Règles Importantes

1. **Idempotence**
   - Les jobs doivent être idempotents
   - Gérer les cas de réexécution
   - Vérifier l'état avant les opérations

2. **Contexte**
   - Toujours fournir un contexte utile
   - Inclure les IDs des entités concernées
   - Ajouter des informations de debug pertinentes

3. **Durée**
   - Les jobs doivent être de longue durée
   - Pour les opérations courtes, utiliser des appels directs
   - Prévoir des timeouts appropriés

4. **Statut**
   - Ne jamais modifier le statut directement
   - Utiliser les méthodes `start()`, `complete()`, `fail()`, `cancel()`
   - Toujours sauvegarder après un changement de statut

5. **Échecs**
   - Capturer et logger toutes les exceptions
   - Fournir des messages d'erreur explicites
   - Conserver le contexte d'échec pour le debug 