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 record ou cours avec seulement get, sans setters publics mutables.
  • Nom Ă  l'impĂ©ratif: reflĂšte l'action de l'entreprise, telle que CreateOrder, UpdateCustomerAddress ou ChangeProductPrice.
  • 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

  • NotEmpty et NotNull pour les champs obligatoires.
  • InclusiveBetween pour les plages numĂ©riques, les valeurs monĂ©taires, les pourcentages, les quantitĂ©s minimales et maximales.
  • MaximumLength, MinimumLength et Length pour la taille de la chaĂźne.
  • RuleForEach pour valider les Ă©lĂ©ments de collection, tels que List<T>.
  • Must pour 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 IUnitOfWork ou 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 Result ou Result<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>
  • -s ou --service: service cible.
  • -m ou --module: module cible dans les services modularisĂ©s.
  • -e ou --entity: entitĂ© associĂ©e Ă  Command.
  • -n, --name, -c ou --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.cs
  • CreateOrderCommandValidator.cs
  • CreateOrderCommandHandler.cs
  • CreateOrderCommandResult.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

  1. Courir lino command new et choisissez le service, le module, l'entité, le type et les propriétés appropriés.
  2. Ouvrez le Command généré et supprimez tout champ qui ne fait pas partie du contrat d'exploitation.
  3. Renforcez validator avec des rĂšgles d’entrĂ©e commerciale qui ne peuvent pas ĂȘtre automatiquement dĂ©duites.
  4. 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.
  5. Utiliser IUnitOfWork pour la persistance et préférez la sauvegarde transactionnelle lorsque l'opération nécessite également des événements ou un Outbox fiable.
  6. Échecs de retour dus Ă  des erreurs Result, et non par des exceptions aux rĂ©sultats commerciaux attendus.
  7. 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 GetCustomerById ou ListOrdersByDateRange.
  • 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

  • GreaterThanOrEqualTo et LessThanOrEqualTo pour les filtres de plage tels que les dates de dĂ©but et de fin.
  • Length, MaximumLength et MinimumLength pour les filtres de texte, tels que le nom, l'e-mail ou le terme de recherche.
  • InclusiveBetween pour la pagination, la limitation page et pageSize.
  • NotEmpty pour les identifiants obligatoires dans les requĂȘtes dĂ©taillĂ©es.
  • Must pour 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>
  • -s ou --service: service cible.
  • -m ou --module: module cible.
  • -e ou --entity: entitĂ© associĂ©e Ă  Query.
  • -n, --name, -q ou --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.cs
  • GetOrderByIdQueryValidator.cs
  • GetOrderByIdQueryHandler.cs
  • GetOrderByIdQueryResult.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

  1. Courir lino query new et choisissez le service, le module, l'entité, le nom Query, le type de retour, le mode de pagination et les propriétés.
  2. Relisez le contrat de demande et ne conservez que les filtres réellement nécessaires pour caller.
  3. 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.
  4. Ajustez la projection handler pour renvoyer uniquement les champs requis par UI, API, l'intégration ou le processus en arriÚre-plan.
  5. Conservez Query read-only : n'appelez pas les méthodes de domaine qui modifient l'état et n'enregistrez pas les modifications dans handler.
  6. Propager les échecs attendus à travers Result, en utilisant les erreurs standardisées existantes, le cas échéant.
  7. 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.

Une erreur non gérée est survenue. Rafraîchir 🗙