Je suis en train de mettre en place une solution dans laquelle j'applique l'architecture de l'oignon et les modèles DDD.
L'un des principes de DDD encourage les entités de domaine à ne disposer que de décodeurs privés et d'un constructeur privé par défaut, afin de garantir que vous ne pouvez pas créer d'entités de domaine dans un état non valide.
Les référentiels contiennent les opérations de données sur les entités de domaine, qui sont mappées depuis / vers la base de données. J'ai essayé les deux approches suivantes:
Les entités de domaine de manière puriste: pas de constructeur par défaut, pas de setters publics; la validation est effectuée dans le ou les constructeurs; qui garantit que vous ne pouvez pas créer une entité de domaine dans un état non valide. L'effet secondaire est qu'il est plus difficile de les dématérialiser dans les référentiels lors d'opérations de lecture. car vous avez besoin de réflexion pour pouvoir créer des instances et des propriétés de carte; et l'utilisation de la dynamique dans les demandes Dapper qui doivent être mappées aux entités de domaine réelles. Si je mappais cela directement aux entités de domaine sans utiliser de dynamique, Dapper lève une exception: il n'y a pas de constructeur public .
Les entités de domaine de manière non puriste: vous autorisez un constructeur par défaut et tous les setters sont publics; afin que vous puissiez créer des entités qui ne sont pas valables à un moment donné. Dans ce cas, vous devez appeler la méthode Validate () manuellement pour vous assurer de leur validité avant de continuer. Cela facilite beaucoup la dématérialisation dans les référentiels, car vous n'avez pas besoin de réflexion ni de dynamique pour mapper la base de données au modèle.
Les deux méthodes fonctionnent, cependant, avec l'option 2, les référentiels deviennent beaucoup plus simples, car ils contiennent beaucoup moins de code de mappage personnalisé, et sans réflexion, ils seront évidemment plus performants. Bien entendu, DDD n'est pas appliqué de manière puriste.
Avant de décider de ce que je vais utiliser dans mon projet, posez-vous une question: existe-t-il d'autres frameworks micro-ORM capables de gérer des constructeurs et des installateurs privés, de sorte que la mise en correspondance de la base de données avec ce type d'entités de domaine «pur» soit prise en charge logique de mappage personnalisé? (Pas d'EF ni de NHibernate, je veux quelque chose de léger).
Ou d’autres solutions techniques pour maintenir l’approche «pure» des entités modèles en combinaison avec un mappage facile des référentiels?
EDIT: la solution que j'ai implémentée était la suivante.
Premièrement, les constructeurs et les setters dans les entités de domaine sont tous «internes», ce qui signifie qu'ils ne peuvent pas être définis par les consommateurs du modèle de domaine. Cependant, j'utilise 'InternalsVisibleTo' pour permettre à la couche d'accès aux données d'y accéder directement, ce qui signifie que la dématérialisation à partir de la base de données est très facile avec Dapper (aucun modèle intermédiaire nécessaire). À partir de la couche d'application, je ne peux utiliser que des méthodes de domaine pour modifier l'entité de domaine, pas les propriétés directement.
Deuxièmement, pour construire de nouvelles entités domein à partir de ma couche d’application, j’ai ajouté des générateurs pour aider à la création d’entités de domaine. Je peux maintenant les construire comme suit:
User user = new UserBuilder()
.WithSubjectId("045454857451245")
.WithDisplayName("Bobby Vinton")
.WithLinkedAccount("Facebook", la => la.WithProviderSubjectId("1548787788877").WithEmailAddress("bobby1@gmail.com"))
.WithLinkedAccount("Microsoft", la => la.WithProviderSubjectId("54546545646").WithEmailAddress("bobby2@gmail.com"))
Lorsque le générateur "construit" l'entité, la validation est également effectuée. Vous ne pouvez donc jamais créer une entité dans un état non valide.
L'un des principes de DDD encourage les entités de domaine à ne disposer que de décodeurs privés et d'un constructeur privé par défaut, afin de garantir que vous ne pouvez pas créer d'entités de domaine dans un état non valide.
Ce n'est pas tout à fait juste. Oui, les modèles de domaines riches n'exposent généralement pas les setters, mais c'est parce qu'ils n'ont pas besoin de setters. Vous indiquez au modèle quoi faire à un niveau d'abstraction plus élevé et lui permettez de déterminer comment ses propres structures de données doivent être modifiées.
De même, il est souvent judicieux d'exposer le constructeur par défaut: si vous considérez un agrégat comme une machine à états finis, le constructeur par défaut est un moyen d'initialiser l'agrégat dans son état "début".
Donc, normalement, vous reconstituez un agrégat de deux manières: soit vous l'initialisez dans son état par défaut, puis vous lui envoyez plusieurs messages, soit vous utilisez le modèle Factory
, comme décrit dans le livre bleu.
cela signifie un mappage supplémentaire entre les deux, ce qui rend le code plus complexe
Peut-être, mais cela garantit également que votre code de domaine est moins dépendant de la magie ORM. En particulier, cela signifie que votre logique de domaine peut fonctionner sur une structure de données différente de celle utilisée pour simplifier la persistance.
Mais ce n'est pas gratuit - vous devez décrire dans le code comment extraire les valeurs de la racine agrégée et les réintroduire dans la base de données (ou dans l'entité ORM, agissant en tant que proxy pour la base de données).
La clé est que vous n'utilisez pas Dapper pour travailler avec vos entités de domaine, mais plutôt dans la couche de référentiel avec des entités POCO. Vos méthodes de référentiel renverront des entités de domaine en convertissant les entités POCO (que Dapper utilise pour l'accès aux données) en entités de domaine.