Recherche/Créer des modules .NET pilotés par le domaine (DDD) sans casser les couches ni les API publiques

Créer des modules .NET pilotés par le domaine (DDD) sans casser les couches ni les API publiques

À utiliser quand une IA modifie une application .NET modulaire pilotée par le domaine. Elle suit les modèles existants, garde les couches à sens unique, place agrégats, dépôts et DTO dans la bonne couche et teste sur toutes les bases de données sans perturber les utilisateurs actuels.

Pour utiliser ces règles d'IA
## Suivre les modèles existants et garder les dépendances entre couches à sens unique Avant de créer du code, recherchez une implémentation comparable dans le dépôt. Suivez les modèles établis de structure, de nommage, de dépendances, de tests et d'extension, sauf si la modification demandée les remplace explicitement. Gardez les diffs ciblés ; évitez les changements accessoires de formatage, de refactorisation ou de dépendances. Utilisez des couches explicites avec des dépendances unidirectionnelles : - **Domain.Shared:** constantes, énumérations, ressources de localisation, contrats d'événements et types partagés sûrs pour toutes les couches et les clients externes. - **Domain:** entités, racines d'agrégats, contrats de dépôt, services de domaine et règles métier. - **Application.Contracts:** interfaces de services applicatifs et objets de transfert de données. - **Application:** implémentations de services applicatifs et orchestration des cas d'utilisation. - **Persistence integrations:** contextes, mappages et implémentations de dépôts propres au fournisseur qui dépendent du domaine. - **HTTP API:** adaptateurs de transport qui dépendent des contrats applicatifs, et non des implémentations. - **Remote client:** mandataires générés ou écrits à la main qui dépendent des contrats applicatifs. - **Web UI:** code de présentation qui utilise la HTTP API. Gardez hors du domaine les préoccupations liées au Web, au transport, au contexte d'authentification et à l'ORM. Maintenez la logique applicative indépendante de la couche Web. Considérez une référence interdite comme un défaut architectural, même si elle raccourcirait une implémentation. Préservez les API publiques et les limites des paquets. N'introduisez aucune modification incompatible, sauf si elle est explicitement demandée, justifiée, accompagnée d'une migration et couverte par des tests de compatibilité. ## Créer des agrégats valides dès le départ et placer les règles entre entités dans des services de domaine Créez les entités dans la couche domaine et rendez-les valides dès leur construction. Le constructeur principal doit imposer les invariants et initialiser toutes les collections détenues. Lorsqu'un ORM l'exige, fournissez un constructeur protégé sans paramètre sans affaiblir les règles normales de création. Acceptez un identifiant de l'appelant au lieu de le générer dans l'entité. Protégez la cohérence avec des setters non publics et des méthodes de domaine dont l'intention est explicite. Rendez les membres substituables lorsque l'environnement d'exécution, le système de mandataires ou le modèle d'extension pris en charge l'exige. Les racines d'agrégats doivent utiliser un identifiant stable plutôt qu'une clé composite, rester petites et référencer les autres racines par identifiant plutôt que par propriété de navigation. Placez les règles interentités appartenant au modèle dans un service de domaine. Ne créez une interface que si plusieurs implémentations, le remplacement ou l'isolation des tests l'exigent réellement. Utilisez un suffixe clair orienté domaine, tel que `Manager`, acceptez des objets de domaine et des primitives plutôt que des DTO, et levez des exceptions métier localisées avec des codes d'erreur uniques et dotés d'un espace de noms lorsque les règles échouent. Les services de domaine peuvent effectuer une recherche, une normalisation, une mise en cache ou une résolution propre au domaine, mais les requêtes simples de persistance appartiennent aux dépôts. Ne lisez pas l'utilisateur authentifié actuel dans le domaine ; transmettez les valeurs nécessaires d'acteur ou de tenant depuis la couche applicative. ## Créer un dépôt par racine d'agrégat, indépendant de la base de données Définissez les contrats de dépôt dans la couche domaine, avec un contrat dédié par racine d'agrégat. Ne créez pas de dépôts pour les entités internes détenues par un agrégat. Maintenez les contrats publics de dépôt indépendants du fournisseur. Privilégiez les fonctions de dépôt élémentaires ou en lecture seule et n'exposez pas aux consommateurs du module une abstraction de fournisseur de requêtes telle que `IQueryable`. Les implémentations du fournisseur peuvent utiliser en interne des API de requête plus riches. Rendez chaque opération de dépôt asynchrone et acceptez un jeton d'annulation facultatif. Pour une méthode qui renvoie un agrégat, activez par défaut le chargement des détails lorsque les appelants ont normalement besoin de l'agrégat complet. Pour les méthodes de liste, désactivez-le par défaut afin d'éviter les joins et transferts de données sans limites. Utilisez des méthodes explicites qui renvoient des agrégats plutôt que des classes composites de projection improvisées. N'ajoutez des méthodes propres à une projection que si des exigences de performance mesurées le justifient. Conservez la logique de chargement anticipé dans un auxiliaire `IncludeDetails` propre au fournisseur afin que les services applicatifs ignorent comment le stockage assemble un agrégat. Transmettez les jetons d'annulation par l'auxiliaire de normalisation du framework lorsqu'il existe. Veillez à ce que les filtres de données du fournisseur, la tenancy et les règles de soft-delete restent actifs lors de l'obtention interne d'un queryable. ## Concevoir les interfaces des services applicatifs avec un DTO distinct par opération Placez une interface par service applicatif dans le paquet de contrats. Utilisez des DTO et des primitives à la frontière ; n'acceptez et ne renvoyez jamais d'entités de domaine. Maintenez les méthodes asynchrones, utilisez un suffixe `Async` cohérent et évitez de répéter le nom de l'entité lorsque le contexte du service est déjà clair. Utilisez des formes CRUD prévisibles : les arguments identifiants restent séparés des DTO de mise à jour ; les opérations de création et de mise à jour renvoient un DTO détaillé lorsqu'il est utile ; les suppressions ne renvoient aucune charge utile ; et les requêtes de liste contiennent des champs facultatifs de filtrage, de tri et de pagination avec une taille maximale de page sûre. Créez un DTO d'entrée distinct pour chaque opération. Ne réutilisez pas les DTO d'entrée uniquement parce que leurs champs correspondent actuellement et évitez l'héritage entre eux. Gardez les DTO exempts de logique métier, validez-les à la frontière et réutilisez des constantes partagées pour les limites. Préférez un DTO de sortie élémentaire et une variante détaillée à de nombreuses formes qui se chevauchent. Les implémentations applicatives orchestrent les cas d'utilisation. Elles doivent employer des dépôts dédiés, charger les agrégats requis, appeler les méthodes de domaine et persister explicitement les mises à jour au lieu de supposer le suivi des modifications. Gardez les requêtes de données hors des services applicatifs. Mappez les propriétés extensibles selon la convention de mapper prise en charge. Ne transmettez pas de types Web, d'abstractions de téléversement ou de flux par les contrats applicatifs. Laissez les adaptateurs de transport convertir les fichiers en une représentation stable au niveau applicatif. Évitez d'appeler un autre service applicatif du même module ; déplacez les règles réutilisables vers le domaine ou un auxiliaire partagé ciblé. Les appels entre modules doivent passer par les contrats publics de l'autre module. Rendez substituables les opérations publiques et les auxiliaires d'extension prévus. Préférez les points d'extension protected aux auxiliaires private lorsque la personnalisation fait partie du contrat du framework. ## Séparer le code de chaque base de données et garder chaque module utilisable seul Fournissez à chaque module une interface de contexte et une implémentation distinctes pour chaque fournisseur de persistance pris en charge. N'exposez les ensembles ou collections que pour les racines d'agrégats. Associez explicitement le nom de connexion du module, rendez configurables les préfixes des tables ou collections et réservez ceux qui appartiennent au framework. Ne dépendez pas du chargement différé. Configurez les mappages dans une extension nommée du générateur de modèle plutôt que de remplir la méthode du cycle de vie du contexte avec les détails des entités. Appliquez le mappage conventionnel du framework à chaque entité avant les règles propres au module. Les implémentations de dépôt propres au fournisseur doivent hériter de sa base standard, utiliser l'interface de contexte comme frontière, transmettre correctement les jetons d'annulation et charger les détails des agrégats de la manière appropriée. Un stockage documentaire peut charger naturellement les collections détenues, tandis qu'un fournisseur relationnel exige des includes explicites ; aucune différence ne doit se propager au domaine ni au contrat applicatif. Chaque paquet doit exposer une classe de composition du module avec des déclarations explicites de dépendances. Enregistrez les services et les options pendant la configuration ; n'utilisez les hooks d'initialisation de l'application que pour les tâches exigeant réellement l'application en fonctionnement. Un module doit rester utilisable seul, sans couplage caché avec un autre paquet. Lorsqu'un module prend en charge plusieurs fournisseurs, exécutez le même contrat de dépôt et de comportement pour chaque intégration. L'indépendance du fournisseur est une propriété testée, pas une convention de nommage. ## Prévoir des points d'extension et retirer les anciennes API par étapes sans les casser Concevez les membres publics et protected pour le modèle d'extension pris en charge par le framework. Utilisez des méthodes substituables, des enregistrements de services remplaçables, des interfaces aux véritables points de substitution et des objets d'options pour la configuration. Documentez les points d'extension et les invariants que les implémentations personnalisées doivent préserver. Ne retirez ni ne renommez un membre public sans cycle d'obsolescence. Marquez l'ancien membre comme obsolete avec des consignes de migration claires, ajoutez des paramètres facultatifs avec des valeurs par défaut et fournissez autant que possible des implémentations par défaut aux nouveaux membres de classes de base. Préférez ajouter une nouvelle interface plutôt que rompre une interface existante. Conservez les chaînes destinées aux utilisateurs dans les ressources de localisation appartenant au paquet de domaine partagé. Utilisez des clés avec espace de noms et des erreurs métier localisées plutôt que du texte codé en dur. Définissez les noms des paramètres, fonctionnalités et autorisations comme des constantes dans leur couche responsable, et organisez-les sous des espaces de noms stables du module. Utilisez un bus d'événements en processus pour les communications au sein d'un processus et un bus distribué entre modules ou services. Placez les contrats d'événements distribués dans le paquet de domaine partagé, gardez-les simples et sérialisables et utilisez des primitives ou des contrats d'événements imbriqués plutôt que des entités de domaine. Gérez les événements d'intégration dans la couche applicative. Traitez la compatibilité, la localisation et le comportement d'extension comme des surfaces publiques du produit. Couvrez leurs modifications avec des tests ciblés et des notes de migration. ## Tester chaque modification sur toutes les bases de données prises en charge et relire le diff final Avant une modification importante de l'architecture ou d'un contrat public, alignez-vous par le processus de conception ou d'issue approuvé du projet. Gardez la pull request ciblée et facile à examiner, et incluez les tests nécessaires pour prouver le comportement affecté. Utilisez des tests unitaires pour les règles de domaine isolées et des tests d'intégration pour les dépôts, les mappages, les autorisations, la validation et la composition des modules. Exercez chaque fournisseur de persistance pris en charge lorsque le contrat revendique l'indépendance du fournisseur. Nommez les tests selon l'opération, le scénario et le résultat attendu. Comparez le diff final à cette liste de contrôle : - Le sens des dépendances est intact, sans propagation vers l'intérieur des préoccupations Web ou de persistance. - Les entités imposent les invariants, les collections détenues sont initialisées et les références d'agrégats utilisent des identifiants. - Les contrats de dépôt restent asynchrones, annulables, centrés sur les agrégats et indépendants du fournisseur. - Les contrats applicatifs utilisent des DTO minimaux et validés ainsi que des formes de méthodes stables. - Les services applicatifs délèguent les requêtes aux dépôts et les mutations aux méthodes de domaine. - Les intégrations relationnelles et documentaires suivent leurs propres conventions de mappage et de chargement des détails. - Les API publiques, clés de localisation, paramètres, fonctionnalités, autorisations et événements restent compatibles. - Les tests couvrent les autorisations, la validation, les cas d'échec, les différences entre fournisseurs et les conditions limites. - Le diff ne contient aucune refactorisation sans rapport, modification de fichier généré ou surface d'API inutile. Indiquez les contrôles ciblés exécutés et tout parcours de fournisseur ou de compatibilité qui reste non vérifié. N'affirmez pas la conformité architecturale sur la seule base de la compilation.