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 🗙