Définir des cas d'utilisation d'applications
Les cas d'utilisation sont les points d'entrée de la couche application qui transforment une intention commerciale en logiciel exécutable. à Lino, ils sont en Application/UseCases et sont organisés autour des entités, aggregates, énumérations, modules et services définis dans les étapes précédentes du projet.
Lino suit un modÚle orienté structure CQRS: Commands représentent les opérations qui changent d'état, tandis que Queries représentent les opérations qui lisent des données. Cette séparation rend les rÚgles d'écriture, les modÚles de lecture, les validations, le suivi, la journalisation et les contrats de réponse explicites et plus faciles à maintenir.
LE Result Pattern standardise le retour des opĂ©rations, en encapsulant le succĂšs, lâĂ©chec, les messages dâerreur et les donnĂ©es rĂ©sultantes. Au lieu d'utiliser des exceptions pour les flux commerciaux attendus, handler renvoie un Result ou Result<T> avec valeur, erreur ou no-content selon le cas.
CLI génÚre les fichiers initiaux pour un cas d'utilisation, mais le code généré ne remplace pas l'analyse de domaine. Le développeur doit encore revoir handler, confirmer les propriétés sélectionnées, ajouter des rÚgles métier, ajuster les validations et vérifier que le cas d'utilisation respecte les limites du module et du service.
Important: Les projets Lino peuvent ĂȘtre créés avec la prise en charge de CQRS et mediator. Commands et Queries gĂ©nĂ©rĂ©s utilisent des abstractions d'application comme ICommand, IQuery, ICommandHandler, IQueryHandler et Tolitech.Results uniformiser le traitement des demandes et les rĂ©sultats des opĂ©rations.
Présentation du cas d'utilisation
Un Cas d'utilisation représente une opération d'application complÚte : reçoit un modÚle d'entrée, valide les données, coordonne les dépendances, invoque le comportement du domaine si nécessaire, conserve ou interroge les données et renvoie un résultat standardisé. C'est là que l'application orchestre un flux métier sans déplacer les invariants en dehors du modÚle de domaine.
Dans les solutions générées par Lino, les cas d'utilisation sont regroupés dans le projet d'application selon une structure prévisible :
src/Services/<ServiceName>/<ModuleName>/Application/UseCases/<EntityName>/
âââ Commands/
â âââ <CommandName>/
âââ Queries/
âââ <QueryName>/
Cette organisation indique clairement à quel service, module et entité appartient chaque fonctionnalité. Il maintient également le modÚle d'écriture et le modÚle de lecture indépendants, ce qui est utile lorsqu'un écran, API, une intégration ou un processus en arriÚre-plan n'a besoin que d'un seul cÎté du comportement.
Command ou Query ?
| Taper | Intention | Exemples | commun Resultado |
|---|---|---|---|
| Command | Modifiez l'état du systÚme. | CreateOrder, UpdateVehicle, DeleteMaintenance, SavePermissionsByRoleId. |
Result, Result<CommandResult> ou no-content en cas de succĂšs. |
| Query | Lire les données sans changer d'état. | GetOrderById, ListCustomers, GetVehicleAvailability. |
Result<QueryResult>, liste, page ou DTO spécifique au consommateur. |
Qu'est-ce qui appartient au cas d'utilisation de l'application
- Contrat d'entrée: le record de command ou query avec uniquement les données nécessaires à l'opération.
- Validation: rÚgles qui vérifient le format de la demande, les champs obligatoires, les plages, les identifiants, les filtres et autres restrictions de saisie avant que handler exécute le flux.
- Orchestration handler: appels aux dĂ©pendances, accĂšs aux rĂ©fĂ©rentiels, requĂȘtes contextuelles, utilisation de Unit of Work, composition des rĂ©sultats, journalisation et traçage.
- Résultat contrat: un petit objet de réponse ou un résultat sans contenu qui donne à caller exactement ce dont il a besoin pour continuer.
Ce qui doit ĂȘtre exclu du cas d'utilisation
- Invariants de domaine doit rester dans les entités, Value Objects, domain services et les méthodes de domaine.
- Détails des infrastructures ils doivent rester derriÚre les abstractions, les référentiels, les contextes de bases de données, les services de fichiers, les intégrations de messagerie et les clients externes.
- ProblÚmes de présentation, tels que l'état UI, les étiquettes, les mises en page et le comportement des composants, doivent rester dans la couche de l'application Web.
Un bon cas d'utilisation est explicite, suffisamment petit pour ĂȘtre compris et strict avec des limites : il coordonne le travail, mais ne devient pas un lieu oĂč toutes les rĂšgles du systĂšme sont mĂ©langĂ©es.
Commands
Un Command est un message immuable qui transporte uniquement les donnĂ©es nĂ©cessaires pour effectuer une action modifiant l'Ă©tat du systĂšme. Des exemples courants sont CreateCustomer, UpdateVehicle, DeleteMaintenance, ConfirmOrder et SavePermissionsByRoleId. Commands doit ĂȘtre nommĂ© actions car elles indiquent Ă l'application de faire quelque chose.
Dans Lino, les Commands générés sont des record qui implémentent l'abstraction de command et renvoient un résultat standardisé. La création Commands renvoie généralement un objet résultat avec l'identifiant ou les informations minimales requises par caller, tandis que la mise à jour et la suppression commands renvoient généralement no-content lorsque l'opération se termine avec succÚs.
Caractéristiques d'un Command
- ImmutabilitĂ©: mis en Ćuvre comme
recordou cours avec seulementget, sans setters publics mutables. - Nom à l'impératif: reflÚte l'action de l'entreprise, telle que
CreateOrder,UpdateCustomerAddressouChangeProductPrice. - Données minimales: contient uniquement les champs nécessaires pour effectuer l'opération, sans transporter d'entités entiÚres ni de gros volumes de données.
- Validation isolĂ©e: chaque Command a ses propres rĂšgles pour garantir que la requĂȘte est cohĂ©rente avant d'atteindre handler.
- Indépendance de UI: Command représente l'intention de l'application, et non le bouton, le formulaire ou le composant qui a déclenché l'opération.
Quand créer un Command
- Utilisez un Command lorsque les données seront créées, modifiées, supprimées, jointes, importées, approuvées, annulées ou mutées de quelque maniÚre que ce soit.
- Gardez payload concentrĂ© sur lâopĂ©ration ; ne transmettez pas une entitĂ© entiĂšre lorsque peu de champs suffisent.
- Choisissez des noms qui décrivent l'action commerciale, et non l'événement visuel qui l'a initiée.
- VĂ©rifiez si l'action appartient au module actuel ou si elle doit ĂȘtre exprimĂ©e par une intĂ©gration, un Ă©vĂ©nement ou une entitĂ© fantĂŽme dans un autre module.
Command Validators
Toi Command Validators assurez-vous que Command est bien formĂ© et rĂ©pond aux conditions dâentrĂ©e avant dâĂȘtre envoyĂ© Ă handler. Dans Lino, ces validations sont implĂ©mentĂ©es avec FluentValidation, une bibliothĂšque commune dans les projets .NET car elle offre un API fluide et lisible pour crĂ©er des rĂšgles.
RĂšgles de validation communes
NotEmptyetNotNullpour les champs obligatoires.InclusiveBetweenpour les plages numériques, les valeurs monétaires, les pourcentages, les quantités minimales et maximales.MaximumLength,MinimumLengthetLengthpour la taille de la chaßne.RuleForEachpour valider les éléments de collection, tels queList<T>.Mustpour les rÚgles personnalisées telles que le format du document, la cohérence des dates et les combinaisons de champs non valides.
validator protĂšge le bord d'attaque. Les invariants de domaine rĂ©els, tels que les frontiĂšres d'Ă©tat, les transitions autorisĂ©es et les rĂšgles qui doivent ĂȘtre valables indĂ©pendamment de caller, continuent d'appartenir Ă aggregate, Ă l'entitĂ©, Ă Value Object ou Ă domain service.
Command Handlers
LE Command Handler exécute la logique d'application associée à Command. Il orchestre les référentiels, les contextes, IUnitOfWork, services auxiliaires, journalisation, traçage, appels d'intégration et événements de domaine si nécessaire.
ModÚle d'implémentation pour un handler
- Recevez des dépendances via l'injection de dépendances, telles que des référentiels, des services externes, des contextes et Unit of Work.
- Valider l'existence et la disponibilité des données nécessaires à l'opération.
- Chargez le aggregate ou l'entité correcte lorsque l'opération dépend de l'état existant.
- Appelez les méthodes de domaine au lieu de dupliquer les invariants dans handler.
- Persister les changements Ă travers
IUnitOfWorkou des abstractions de persistance de projet. - Enregistrez le domaine, Outbox ou les événements d'intégration lorsque l'opération doit communiquer avec d'autres parties du systÚme.
- Retour
ResultouResult<T>succÚs, échec connu ou données de réponse minimales.
Command Results et Result Pattern
LE Command Result devrait ĂȘtre un simple DTO avec juste les donnĂ©es nĂ©cessaires pour que caller continue. Dans la crĂ©ation, cela inclut gĂ©nĂ©ralement le Id gĂ©nĂ©rĂ©. Lors de la mise Ă jour ou de la suppression, il se peut qu'il n'y ait pas de payload. Lorsque l'opĂ©ration Ă©choue en raison d'une condition attendue, le retour doit contenir des informations d'erreur standardisĂ©es.
LE Result Pattern encapsule le rĂ©sultat dâune opĂ©ration comme succĂšs ou Ă©chec. Au lieu de lever des exceptions pour des scĂ©narios prĂ©visibles comme une entitĂ© introuvable, un Ă©tat invalide ou une validation mĂ©tier, handler renvoie un type comme Result<T> avec valeur, erreur et mĂ©tadonnĂ©es si nĂ©cessaire.
- En cas de succÚs, le résultat peut révéler un
Value, comme un petit DTO ou un identifiant spécialement conçu. - En cas d'échec, le résultat stocke des codes ou des messages standardisés, souvent définis dans des définitions d'erreur réutilisables.
- Le flux de gestion des erreurs est cohérent entre Application, API, typed clients et UI.
Créer un Command avec CLI
Lino simplifie la génération des artefacts nécessaires pour un nouveau Command via la commande :
lino command new
CLI prend également en charge les options permettant de réduire les questions dans l'assistant :
lino command new --service <ServiceName> --module <ModuleName> --entity <EntityName> --name <CommandName> lino command new --name <CommandName> --service <ServiceName> --module <ModuleName> --entity <EntityName> lino command list --service <ServiceName> --module <ModuleName> --entity <EntityName>
-sou--service: service cible.-mou--module: module cible dans les services modularisés.-eou--entity: entité associée à Command.-n,--name,-cou--command: nom de Command.
Lors du flux interactif, Lino confirme le service, le module, l'entitĂ©, le nom Command, le type Command et, pour les opĂ©rations de crĂ©ation ou de mise Ă jour, les propriĂ©tĂ©s qui feront partie de la requĂȘte. Les types exposĂ©s par CLI sont CrĂ©er, Mise Ă jour et Supprimer.
AprÚs confirmation, Lino crée des fichiers comme :
CreateOrderCommand.csCreateOrderCommandValidator.csCreateOrderCommandHandler.csCreateOrderCommandResult.cs
Exemple de structure générée
Considérez Command CreatePerson. La structure générée ressemblera à  :
<ProjectName>/
âââ src/
âââ Services/
âââ <ServiceName>/
âââ Application/
âââ <ProjectName>.<ServiceName>.Application.csproj
âââ UseCases/
âââ People/
âââ Commands/
â âââ CreatePerson/
â âââ CreatePersonCommand.cs
â âââ CreatePersonCommandValidator.cs
â âââ CreatePersonCommandHandler.cs
â âââ CreatePersonCommandResult.cs
âââ Queries/
âââ ...
Pour une flotte personnalisĂ©e Command, la structure suit le mĂȘme modĂšle :
Application/UseCases/Vehicles/Commands/UpdateVehicle/ âââ UpdateVehicleCommand.cs âââ UpdateVehicleCommandValidator.cs âââ UpdateVehicleCommandHandler.cs âââ UpdateVehicleCommandResult.cs
Responsabilités des fichiers Command
- Command: contrat de demande immuable à l'entrée de l'opération.
- Validator: validation des entrées, généralement avec des rÚgles obligatoires, taille, plage, identifiants et collections.
- Handler: orchestration des référentiels, Unit of Work, contextes, méthodes de domaine, journalisation, traçage et création de résultats.
- Result: Contrat de réponse minimum pour les opérations réussies qui doivent renvoyer des données.
Liste de contrĂŽle de mise en Ćuvre
- Courir
lino command newet choisissez le service, le module, l'entité, le type et les propriétés appropriés. - Ouvrez le Command généré et supprimez tout champ qui ne fait pas partie du contrat d'exploitation.
- Renforcez validator avec des rĂšgles dâentrĂ©e commerciale qui ne peuvent pas ĂȘtre automatiquement dĂ©duites.
- Examinez handler et assurez-vous qu'il invoque le comportement du domaine plutĂŽt que de dupliquer les invariants au niveau de la couche application.
- Utiliser
IUnitOfWorkpour la persistance et prĂ©fĂ©rez la sauvegarde transactionnelle lorsque l'opĂ©ration nĂ©cessite Ă©galement des Ă©vĂ©nements ou un Outbox fiable. - Ăchecs de retour dus Ă des erreurs
Result, et non par des exceptions aux résultats commerciaux attendus. - Créez et testez le point de terminaison, la page ou l'intégration qui appellera Command.
RÚgle générale : le générateur délivre le squelette architectural. Le correctif final vient de l'examen du cas d'utilisation par rapport au langage du domaine, aux invariants, aux besoins de persistance et aux limites des modules.
Queries
Un Query reprĂ©sente lâintention dâobtenir des donnĂ©es sans changer lâĂ©tat du domaine. Queries sont conçus pour une lecture efficace, renvoyant exactement les champs nĂ©cessaires Ă caller, sans charger des entitĂ©s entiĂšres lorsque cela n'est pas nĂ©cessaire.
Des exemples courants sont GetCustomerById, ListCustomers, ListPhoneTypes, ListOrdersByDateRange et un query personnalisĂ© comme GetVehicleAvailability. Un Query doit rĂ©pondre Ă une question spĂ©cifique Ă lâapplication.
Caractéristiques d'un Query
- Immuable: Tout comme Commands, un Query ne doit pas autoriser les modifications une fois créé.
- Nom descriptif: reflÚte les informations recherchées, telles que
GetCustomerByIdouListOrdersByDateRange. - Filtres et pagination: peut charger les dates, le statut, la page, la taille de la page, le texte de recherche, l'ordre et d'autres paramĂštres de lecture.
- Projection: doit renvoyer DTO ou afficher le modÚle avec uniquement les champs nécessaires, en évitant l'exposition directe des entités de domaine.
- Aucun effet secondaire: vous ne devez pas appeler de méthodes qui changent d'état ou enregistrer les modifications dans la base de données.
Quand créer un Query
- Utilisez un Query lorsque l'opération lit des données et ne doit pas modifier l'état.
- Utilisez un Query à résultat unique pour les écrans de détails, les recherches d'identifiants, les contrÎles de disponibilité ou les réponses d'objets.
- Utilisez une liste Query pour les grilles, les listes de sélection, les collections enfants et les contrÎles de sélection.
- Utilisez la pagination pour les grilles et les ensembles de données potentiellement volumineux.
- Utilisez des listes simples pour les petites données de référence, les énumérations et les points de terminaison des options.
Query Validators
Toi Query Validators validez les paramĂštres d'entrĂ©e tels que les filtres, les valeurs de pagination, les plages de dates, les identifiants et les rĂšgles de visibilitĂ©. Ils sont Ă©galement mis en Ćuvre avec FluentValidation.
RĂšgles de validation courantes dans Queries
GreaterThanOrEqualToetLessThanOrEqualTopour les filtres de plage tels que les dates de dĂ©but et de fin.Length,MaximumLengthetMinimumLengthpour les filtres de texte, tels que le nom, l'e-mail ou le terme de recherche.InclusiveBetweenpour la pagination, la limitationpageetpageSize.NotEmptypour les identifiants obligatoires dans les requĂȘtes dĂ©taillĂ©es.Mustpour les combinaisons de filtres non valides, telles que la date de fin infĂ©rieure Ă la date de dĂ©but.
Query Handlers
LE Query Handler interroge les référentiels ou le contexte de la base de données et renvoie des projections optimisées. Dans Lino, il est recommandé d'utiliser des projections avec Select, des filtres explicites, un ordre prévisible et des lectures non suivies le cas échéant.
Le handler de Query doit ĂȘtre read-only : il n'appelle pas de mĂ©thodes de domaine changeant d'Ă©tat, ne dĂ©clenche pas de modifications persistantes et ne s'exĂ©cute pas. SaveChanges. Si une lecture doit enregistrer un audit, lancer une intĂ©gration ou recalculer l'Ă©tat, cela indique gĂ©nĂ©ralement un autre cas d'utilisation ou un processus distinct.
Query Results
Dans Lino, les résultats de Queries sont représentés par des record nommés avec le suffixe QueryResult. Cette convention s'applique aux réponses uniques, aux listes simples, aux listes paginées ou aux processus DTO, API spécifiques à l'écran, aux processus d'intégration ou d'arriÚre-plan.
Les donnĂ©es de requĂȘte Result doivent ĂȘtre des contrats stables en lecture. Traitez-les comme DTOs conçu pour le consommateur, et non comme un raccourci pour exposer directement les entitĂ©s de domaine.
Créer un Query avec CLI
Semblable à Commands, Lino fournit la commande :
lino query new
CLI accepte également des options telles que :
lino query new --service <ServiceName> --module <ModuleName> --entity <EntityName> --name <QueryName> lino query new --name <QueryName> --service <ServiceName> --module <ModuleName> --entity <EntityName> lino query list --service <ServiceName> --module <ModuleName> --entity <EntityName>
-sou--service: service cible.-mou--module: module cible.-eou--entity: entité associée à Query.-n,--name,-qou--query: nom de Query.
Pendant le flux interactif, Lino demande si Query renvoie un rĂ©sultat unique ou une liste. Lorsque la rĂ©ponse est une liste, il demande Ă©galement si elle doit ĂȘtre paginĂ©e. Enfin, il permet de sĂ©lectionner les propriĂ©tĂ©s qui doivent ĂȘtre renvoyĂ©es ou utilisĂ©es par la projection gĂ©nĂ©rĂ©e.
Lino générera automatiquement :
GetOrderByIdQuery.csGetOrderByIdQueryValidator.csGetOrderByIdQueryHandler.csGetOrderByIdQueryResult.cs
Exemple de structure générée pour Queries
<ProjectName>/
âââ src/
âââ Services/
âââ <ServiceName>/
âââ Application/
âââ <ProjectName>.<ServiceName>.Application.csproj
âââ UseCases/
âââ Orders/
âââ Commands/
| âââ ...
âââ Queries/
âââ GetOrderById/
âââ GetOrderByIdQuery.cs
âââ GetOrderByIdQueryValidator.cs
âââ GetOrderByIdQueryHandler.cs
âââ GetOrderByIdQueryResult.cs
Pour une disponibilitĂ© de vĂ©hicule personnalisĂ©e Query, la structure suit le mĂȘme schĂ©ma :
Application/UseCases/Vehicles/Queries/GetVehicleAvailability/ âââ GetVehicleAvailabilityQuery.cs âââ GetVehicleAvailabilityQueryValidator.cs âââ GetVehicleAvailabilityQueryHandler.cs âââ GetVehicleAvailabilityQueryResult.cs
Responsabilités des fichiers Query
- Query: contrat de requĂȘte immuable avec identifiants, filtres, ordre, pagination ou plages de dates.
- Validator: validation des identifiants, pagination, filtres obligatoires, plages de dates et critĂšres de recherche.
- Handler: lecture de l'orchestration utilisant le contexte d'application, les projections, les filtres, l'ordre, la pagination, la journalisation, le traçage et la création de résultats.
- Result: réponse DTO calquée sur caller, souvent avec des record internes pour les éléments de la liste.
Liste de contrĂŽle de mise en Ćuvre
- Courir
lino query newet choisissez le service, le module, l'entité, le nom Query, le type de retour, le mode de pagination et les propriétés. - Relisez le contrat de demande et ne conservez que les filtres réellement nécessaires pour caller.
- Renforcez validator, en particulier pour les identifiants requis, les plages de dates, les limites de taille de page et les combinaisons de filtres non valides.
- Ajustez la projection handler pour renvoyer uniquement les champs requis par UI, API, l'intégration ou le processus en arriÚre-plan.
- Conservez Query read-only : n'appelez pas les méthodes de domaine qui modifient l'état et n'enregistrez pas les modifications dans handler.
- Propager les échecs attendus à travers
Result, en utilisant les erreurs standardisées existantes, le cas échéant. - Créez et testez le consommateur qui appelle Query, tel que le point de terminaison API, la page Blazor, l'intégration en cours ou typed client.
Exemple de Query personnalisé
Dans le flux d'intĂ©gration entre les modules SaaS, une disponibilitĂ© personnalisĂ©e du vĂ©hicule Query peut ĂȘtre créée avec lino query new. La structure gĂ©nĂ©rĂ©e fournit Query, Handler, Result et Validator. Ensuite, le dĂ©veloppeur ajoute les entrĂ©es nĂ©cessaires, telles que l'identifiant du vĂ©hicule, la date de dĂ©but et la date de fin, valide que la plage est correcte et implĂ©mente la logique handler. C'est le flux attendu : gĂ©nĂ©rez d'abord la structure cohĂ©rente, puis complĂ©tez le comportement spĂ©cifique au domaine avec du code explicite.
Lire les conseils du modĂšle : les rĂ©sultats de Query doivent ĂȘtre des contrats stables. Traitez-les comme DTOs conçu pour caller, et non comme un raccourci pour exposer directement les entitĂ©s de domaine.
Conclusion
Définir des cas d'utilisation dans Lino transforme le modÚle de domaine en opérations d'application claires. Commands gÚre les changements d'état, Queries gÚre les lectures, validators protÚge le front d'entrée, handlers orchestre le flux et les objets de résultat standardisent la sortie.
Le flux le plus rapide a tendance Ă ĂȘtre : modĂ©liser le domaine, gĂ©nĂ©rer les Commands et Queries nĂ©cessaires, examiner les fichiers gĂ©nĂ©rĂ©s, complĂ©ter le comportement commercial, exposer le cas d'utilisation via un API ou une page si nĂ©cessaire et valider le tout avec la construction et les tests. Cela permet un dĂ©veloppement rapide sans perdre le contrĂŽle architectural.
Au fur et à mesure que le projet se développe, concentrez chaque cas d'utilisation sur une intention commerciale, respectez les limites des services et des modules et utilisez des événements, des intégrations ou des entités fantÎmes lorsqu'un autre module doit participer au processus.
