Définition des cas d'utilisation de l'application

Dans cette section, nous allons explorer comment définir les cas d’utilisation de votre application à l’aide de Lino CLI. Les cas d’utilisation représentent des interactions spécifiques entre les utilisateurs ou les systèmes externes et l’application, en encapsulant les règles métier et en promouvant une architecture orientée domaine. Nous aborderons la séparation des opérations de lecture et d’écriture à travers le pattern CQRS, ainsi que la standardisation des réponses en utilisant le Result Pattern.


Le pattern CQRS (Command Query Responsibility Segregation) propose de séparer les opérations qui modifient l’état de l’application (Commands) de celles qui ne font que consulter les données (Queries). Cette approche permet d’optimiser chaque type d’opération de manière indépendante, améliorant ainsi l’évolutivité, les performances et la maintenabilité du système.


Le Result Pattern est utilisé pour standardiser le retour des opérations, en encapsulant des informations sur le succès ou l’échec, les messages d’erreur et les données résultantes. Cela facilite la gestion cohérente des réponses à travers les différentes couches de l’application.


Nous verrons comment sont structurés les fichiers de Commands et de Queries, la validation des données, comment la logique métier est appliquée dans les Handlers, et comment retourner des résultats de manière standardisée. Tout cela est réalisé grâce à la génération automatique de ces composants via le CLI de Lino.

Remarque : Bien que cela ne soit pas obligatoire, Lino propose actuellement deux options pour appliquer le pattern CQRS dans la couche applicative : en utilisant le pattern Mediator via la bibliothèque MediatR (de Jimmy Bogard) ou la bibliothèque Mediator (de Martin Othamar).

Vue d’ensemble des cas d’utilisation

Un cas d’utilisation représente une interaction complète entre des utilisateurs ou des systèmes externes et l’application, décrivant des scénarios métier spécifiques. Dans Lino, chaque cas d’utilisation est divisé en :

  • Command : reprĂ©sente l’intention de modifier l’état du système (crĂ©er, mettre Ă  jour, supprimer, etc.).
  • Query : reprĂ©sente l’intention de consulter des donnĂ©es sans modifier l’état du domaine.

Cette séparation favorise la clarté du code, facilite les tests, permet une scalabilité indépendante pour la lecture et l’écriture, et s’aligne sur les principes de l’architecture propre et les bonnes pratiques du Domain-Driven Design (DDD).

Commandes

Une Commande est un message immuable qui contient uniquement les données nécessaires pour exécuter une action modifiant l’état du système (par exemple, CreateInvoice, DeactivateUser). Elle doit contenir uniquement les propriétés représentant les informations essentielles à l’exécution de l’opération.

Caractéristiques d’une Commande

  • ImmutabilitĂ© : implĂ©mentĂ©e comme un record ou une classe avec uniquement des get, sans setters publics.
  • Nom Ă  l’impĂ©ratif : reflète l’action qui sera exĂ©cutĂ©e, par exemple CreateOrder, UpdateCustomerAddress.
  • DonnĂ©es minimales : contient uniquement les champs nĂ©cessaires Ă  l’exĂ©cution de l’opĂ©ration, sans retourner de grandes quantitĂ©s de donnĂ©es.
  • Validation isolĂ©e : chaque Commande possède ses propres règles de validation, garantissant sa cohĂ©rence avant d’atteindre le gestionnaire (Handler).

Validateurs de Commande

Les Validateurs de Commande s’assurent que la Commande est bien formée et respecte les exigences métier avant d’être envoyée au gestionnaire. Dans Lino, nous utilisons la bibliothèque FluentValidation pour implémenter ces validations, car elle est largement adoptée dans les projets .NET et offre une API Fluent pour la création des règles.

Règles communes de validation

  • NotEmpty, NotNull pour les champs obligatoires.
  • InclusiveBetween pour les plages numĂ©riques (ex. : valeurs monĂ©taires, quantitĂ©s minimum/maximum).
  • MaximumLength et MinimumLength pour la longueur des chaĂ®nes de caractères.
  • RuleForEach pour la validation des Ă©lĂ©ments dans les collections (List<T>).
  • Must pour les règles personnalisĂ©es (ex. : format des documents, validation des dates).

Gestionnaires de Commande

Le Gestionnaire de Commande est responsable d’exécuter la logique métier associée à cette Commande. Il orchestre les repositories, les unités de travail (IUnitOfWork), les services auxiliaires et déclenche les événements de domaine si nécessaire.

Modèle d’implémentation d’un gestionnaire

  • Recevoir les dĂ©pendances (repositories, services externes, UnitOfWork) via injection de dĂ©pendances.
  • Mapper et instancier les entitĂ©s du domaine, garantissant la cohĂ©rence initiale.
  • Appliquer les règles mĂ©tier (validations supplĂ©mentaires, calcul des valeurs, dĂ©clenchement d’évĂ©nements de domaine).
  • Persister les modifications via IUnitOfWork ou des repositories directs.
  • Retourner un RĂ©sultat de Commande, indiquant succès ou Ă©chec (Result<T>).

Résultats de Commande et Result Pattern

Le Résultat de Commande doit être un DTO simple contenant uniquement les données minimales nécessaires pour que l’appelant (par exemple, une API ou un front-end) puisse continuer. Il inclut généralement l’identifiant de l’entité créée ou mise à jour (Id) et, en cas d’échec, des informations standardisées sur l’erreur.

Pour les scénarios où la Commande peut échouer, nous utilisons le Result Pattern : une abstraction qui encapsule le résultat d’une opération, pouvant être un succès ou un échec. En .NET, il est courant d’utiliser des types comme Result<T> (ou des bibliothèques similaires), qui :

  • Permettent de dĂ©finir une Value en cas de succès (par exemple, un DTO simple).
  • En cas d’échec, stockent des codes ou messages standardisĂ©s (Error), Ă©ventuellement avec des mĂ©tadonnĂ©es supplĂ©mentaires (codes HTTP, dĂ©tails de validation, etc.).
  • Facilitent l’unification du flux de gestion des erreurs Ă  travers toute l’API, maintenant la cohĂ©rence entre les couches.

Créer une Commande avec le CLI

Lino simplifie la génération de tous les artefacts nécessaires pour une nouvelle Commande via la commande :

lino command new

Lors de l’exécution de cette commande, l’assistant interactif demandera :

  • Service — Nom du service oĂą la Commande sera créée.
  • Module — Dans les services modulaires, dĂ©finit le module cible.
  • EntitĂ© — EntitĂ© de domaine liĂ©e Ă  la Commande (optionnelle mais recommandĂ©e).
  • Nom de la Commande — Nom final de la Commande (par exemple, CreateOrder).

Après confirmation, Lino créera automatiquement les fichiers :

  • CreateOrderCommand.cs
  • CreateOrderCommandValidator.cs
  • CreateOrderCommandHandler.cs
  • CreateOrderCommandResult.cs

Exemple de structure générée

Considérez la Commande CreatePerson. La structure générée ressemblera à :

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── People/
                        ├── Commands/
                        │   └── CreatePerson/
                        │       ├── CreatePersonCommand.cs
                        │       ├── CreatePersonCommandValidator.cs
                        │       ├── CreatePersonCommandHandler.cs
                        │       └── CreatePersonCommandResult.cs
                        └── Queries/
                            └── ...

RequĂŞtes

Une requête représente l’intention d’obtenir des données sans modifier l’état du domaine. Les requêtes sont conçues pour être efficaces en lecture, en retournant uniquement les champs nécessaires, sans charger des entités complètes lorsqu’elles ne sont pas demandées.

Caractéristiques d’une Requête

  • Immuable — comme les Commandes, une RequĂŞte ne doit pas ĂŞtre modifiĂ©e après sa crĂ©ation.
  • Nom descriptif — reflète les informations recherchĂ©es, par exemple : GetCustomerById, ListOrdersByDateRange.
  • Filtres et Pagination — peut recevoir des paramètres pour appliquer des recherches spĂ©cifiques (dates, statut, page, tri).
  • Projection — doit retourner un DTO ou un modèle de vue contenant uniquement les champs nĂ©cessaires, Ă©vitant le chargement complet des objets de domaine.

Validateurs de RequĂŞte

Les validateurs de requête ont pour responsabilité de valider les paramètres d’entrée, tels que les filtres, les valeurs de pagination (page, pageSize) et les règles de sécurité (autorisations utilisateur, visibilité des données). Ils sont également implémentés avec FluentValidation.

Règles de Validation Courantes pour les Requêtes

  • GreaterThanOrEqualTo ou LessThanOrEqualTo pour les filtres par plage (ex. : dates).
  • Length pour les filtres textuels (ex. : recherche par nom ou e-mail).
  • InclusiveBetween pour les paramètres de pagination (ex. : nombre minimum/maximum d’élĂ©ments par page).

Gestionnaires de RequĂŞte

Le gestionnaire de requête est responsable d’interroger les dépôts ou le contexte de base de données et de retourner des projections optimisées, en évitant le chargement complet des entités du domaine. Dans Lino, il est recommandé d’utiliser des projections avec des méthodes comme .Select() pour mapper directement les données dans le format attendu du DTO, garantissant ainsi efficacité et meilleures performances.

Résultats de Requête

Dans Lino, les résultats des requêtes sont toujours représentés par des records nommés avec le suffixe QueryResult. Cette convention s’applique quel que soit le type de réponse — qu’il s’agisse d’une liste d’éléments (avec ou sans pagination) ou d’un élément unique détaillé.

Créer une Requête avec le CLI

Comme pour les Commandes, Lino fournit la commande suivante :

lino query new

L’assistant vous demandera :

  • Service — le service dans lequel la requĂŞte sera créée.
  • Module — si applicable, dĂ©finit le module du domaine.
  • EntitĂ© — l’entitĂ© ou l’agrĂ©gat auquel la requĂŞte est associĂ©e (facultatif).
  • Nom de la RequĂŞte — par exemple : GetOrderById ou ListProductsByCategory.

Lino générera automatiquement :

  • GetOrderByIdQuery.cs
  • GetOrderByIdQueryValidator.cs
  • GetOrderByIdQueryHandler.cs
  • GetOrderByIdQueryResult.cs

Exemple de Structure Générée pour les Requêtes

MyApp/
└── src/
    └── Services/
        └── MyService/
            └── Application/
                ├── MyApp.MyService.Application.csproj
                └── UseCases/
                    └── Orders/
                        ├── Commands/
                        |   └── ...
                        └── Queries/
                            └── GetOrderById/
                                ├── GetOrderByIdQuery.cs
                                ├── GetOrderByIdQueryValidator.cs
                                ├── GetOrderByIdQueryHandler.cs
                                └── GetOrderByIdQueryResult.cs

Conclusion

Après avoir compris comment définir et structurer les Use Cases (Commands et Queries) dans Lino, vous êtes prêt à créer des scénarios métier robustes et évolutifs, en segmentant clairement la logique d’écriture et de lecture. Utilisez le CLI pour accélérer le développement, mais pensez toujours à revoir et ajuster les implémentations selon les besoins spécifiques de votre domaine.

Ensuite, explorez comment gérer la persistance, les événements de domaine et les services d’infrastructure pour composer toute l’architecture de votre application.

Une erreur non gérée est survenue. Rafraîchir đź—™