Patterns de développement
Documentation complète des patterns utilisés dans le projet pour garantir la cohérence et la maintenabilité du code.
Pattern Action
Le pattern Action encapsule une opération métier atomique et réutilisable. Chaque Action représente une intention métier claire (créer, modifier, supprimer, etc.).
Principes
- Atomicité : Une Action = une opération métier
- Réutilisabilité : Utilisable depuis n'importe où (Controller, Livewire, Commande, etc.)
- Testabilité : Facile à tester en isolation
- Transaction : Utilise les transactions DB pour garantir la cohérence
Convention de nommage
Format : \{Verb\}\{Entity\}Action
// Exemples
CreateFeedbackAction
UpdateFeedbackStatusAction
DeleteFeedbackAction
PublishProductAction
Structure de base
namespace App\Specifics\{ModuleName}\Actions;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class CreateFeedbackAction
{
public function execute(User $user, array $data): Feedback
{
return DB::transaction(function () use ($user, $data) {
$feedback = Feedback::create([
'user_id' => $user->id,
'status' => FeedbackStatus::OPEN,
...$data,
]);
event(new FeedbackCreated($feedback));
return $feedback;
});
}
}
Bonnes pratiques
- ✅ Utiliser
DB::transaction()pour les opérations critiques - ✅ Typage strict des paramètres et du retour
- ✅ Déclencher des Events après les actions importantes
- ✅ Gérer les erreurs explicitement avec des exceptions
- ❌ Éviter les Actions trop complexes (utiliser un Service)
Pattern Query
Le pattern Query encapsule la logique de récupération de données complexe. Chaque Query représente une requête spécifique avec filtres, tri, pagination, etc.
Principes
- Spécificité : Une Query = une requête spécifique
- Réutilisabilité : Utilisable depuis n'importe où
- Performance : Optimisée avec eager loading
- Flexibilité : Paramètres optionnels pour filtres
Convention de nommage
Format : Get\{Entity\}\{Context\}Query
// Exemples
GetFeedbackListQuery
GetFeedbackDetailQuery
GetPublishedPostsQuery
GetUserInvoicesQuery
Structure de base
namespace App\Specifics\{ModuleName}\Queries;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
class GetFeedbackListQuery
{
public function execute(
?FeedbackType $type = null,
?FeedbackStatus $status = null,
?User $user = null,
string $sortBy = 'created_at',
string $sortDirection = 'desc',
int $perPage = 15
): LengthAwarePaginator {
$query = Feedback::with(['user'])
->withCount(['votes', 'comments']);
if ($type) {
$query->where('type', $type);
}
return $query->orderBy($sortBy, $sortDirection)
->paginate($perPage);
}
}
Bonnes pratiques
- ✅ Utiliser
with()pour l'eager loading - ✅ Utiliser
withCount()pour les statistiques - ✅ Paramètres optionnels avec valeurs par défaut
- ✅ Pagination pour les listes
- ❌ Éviter les requêtes N+1
Pattern Service
Le pattern Service orchestre plusieurs Actions et/ou Queries pour réaliser une opération métier complexe. Un Service coordonne la logique entre plusieurs composants.
Principes
- Orchestration : Coordonne plusieurs Actions/Queries
- Logique métier complexe : Gère les cas complexes
- Réutilisabilité : Encapsule la logique réutilisable
- Dependency Injection : Injecte les Actions/Queries nécessaires
Convention de nommage
Format : \{Entity\}Service ou \{Purpose\}Service
// Exemples
FeedbackService
ProductService
BlockManager
ContactSyncService
Structure de base
namespace App\Specifics\{ModuleName}\Services;
class FeedbackService
{
public function __construct(
private CreateFeedbackAction $createAction,
private GetFeedbackListQuery $listQuery,
) {}
public function createFeedback(User $user, array $data): Feedback
{
return $this->createAction->execute($user, $data);
}
public function listFeedbacks(array $filters = []): LengthAwarePaginator
{
return $this->listQuery->execute(...$filters);
}
}
Quand utiliser un Service
- ✅ Orchestrer plusieurs Actions/Queries
- ✅ Logique métier complexe
- ✅ Synchronisation de données
- ❌ Opérations atomiques simples (utiliser une Action)
- ❌ Requêtes simples (utiliser une Query)
Pattern DTO (Data Transfer Object)
Le pattern DTO encapsule des données complexes pour les transférer entre les couches de l'application. Un DTO garantit la structure et la validation des données.
Principes
- Immutabilité : Propriétés
readonlypour garantir l'immutabilité - Typage strict : Types explicites pour toutes les propriétés
- Validation : Structure garantie
- Sérialisation : Méthode
toArray()pour la conversion
Convention de nommage
Format : \{Action\}\{Entity\}Data
// Exemples
CreateRoomData
UpdateProductData
RoomTokenData
Structure de base
namespace App\Specifics\{ModuleName}\DTO;
class CreateRoomData
{
public function __construct(
public readonly string $name,
public readonly ?string $description = null,
public readonly array $metadata = [],
) {}
public function toArray(): array
{
return [
'name' => $this->name,
'description' => $this->description,
'metadata' => $this->metadata,
];
}
public static function fromArray(array $data): self
{
return new self(
name: $data['name'],
description: $data['description'] ?? null,
metadata: $data['metadata'] ?? [],
);
}
}
Quand utiliser un DTO
- ✅ Transférer des données complexes entre couches
- ✅ Garantir la structure des données
- ✅ Validation centralisée
- ❌ Données simples (utiliser un tableau associatif)
- ❌ Logique métier (utiliser une Action/Service)
Ressources supplémentaires
Pour plus de détails sur chaque pattern, consultez la documentation complète dans docs/patterns/.