Modèle de domaine riche avec comportements et ORM

Après avoir visionné la présentation de NDC12 “Crafting Wicked Domain Models” de Jimmy Bogard ( http://ndcoslo.oktaset.com/Agenda ), je cherchais comment conserver ce type de modèle de domaine.
Ceci est un exemple de classe de présentation:

public class Member { List _offers; public Member(ssortingng firstName, ssortingng lastName) { FirstName = firstName; LastName = lastName; _offers = new List(); } public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public IEnumerable AssignedOffers { get { return _offers; } } public int NumberOfOffers { get; private set; } public Offer AssignOffer(OfferType offerType, IOfferValueCalc valueCalc) { var value = valueCalc.CalculateValue(this, offerType); var expiration = offerType.CalculateExpiration(); var offer = new Offer(this, offerType, expiration, value); _offers.Add(offer); NumberOfOffers++; return offer; } } 

il y a donc quelques règles contenues dans ce modèle de domaine:
– Le membre doit avoir son prénom et son nom de famille
– Le nombre d’offres ne peut pas être changé à l’extérieur
– Le membre est responsable de la création d’une nouvelle offre, du calcul de sa valeur et de sa cession

Si vous essayez de mapper ceci à un ORM comme Entity Framework ou NHibernate, cela ne fonctionnera pas. Quelle est donc la meilleure approche pour mapper ce type de modèle sur une firebase database avec ORM?
Par exemple, comment charger AssignedOffers à partir de la firebase database s’il n’y a pas de programme de configuration?

Pour moi, la seule chose qui ait du sens est l’utilisation d’une architecture de commandes / requêtes: les requêtes sont toujours effectuées avec DTO, pas les entités de domaine, et les commandes sont exécutées sur des modèles de domaine. En outre, l’approvisionnement en événements convient parfaitement aux comportements sur le modèle de domaine. Mais ce type d’architecture CQS ne convient peut-être pas à tous les projets, spécialement les sites contaminés. Ou pas?

Je suis au courant de questions similaires, mais je n’ai pas pu trouver d’exemple concret ni de solution.

C’est en fait une très bonne question et quelque chose que j’ai envisagé. Il est potentiellement difficile de créer des objects de domaine appropriés entièrement encapsulés (c’est-à-dire sans parameters de propriété) et d’utiliser un ORM pour créer directement les objects de domaine.

D’après mon expérience, il existe 3 façons de résoudre ce problème:

  • Comme déjà mentionné par Luka, NHibernate prend en charge la cartographie sur des champs privés plutôt que sur les propriétaires.
  • Si vous utilisez EF (ce qui, à mon avis, ne prend pas en charge ce qui précède), vous pouvez utiliser le modèle mémento pour restaurer l’état de vos objects de domaine. vous utilisez par exemple une structure d’entité pour renseigner des objects ‘memento’ que vos entités de domaine acceptent de définir pour leurs champs privés.
  • Comme vous l’avez souligné, l’utilisation de CQRS avec la détermination de l’événement élimine ce problème. C’est ma méthode préférée pour créer des objects de domaine parfaitement encapsulés, qui présentent également tous les avantages supplémentaires de la source d’événements.

Vieux fil. Mais il y a un article plus récent (fin 2014) de Vaughn Vernon qui traite uniquement de ce scénario, avec une référence particulière à Entity Framework. Étant donné que j’ai eu du mal à trouver de telles informations, il pourrait être utile de les publier ici aussi.

Fondamentalement, la publication préconise que l’object Domaine ( Product ( Product ) ProductState l’object de données ProductState EF ProductState pour ce qui concerne le côté “sac” . Bien entendu, l’object de domaine appendait tout son comportement de domaine enrichi via des méthodes / accesseurs spécifiques à un domaine, mais il aurait recours à un object de données interne lorsqu’il devait obtenir / définir ses propriétés.

Copier un extrait directement de la poste:

 public class Product { public Product( TenantId tenantId, ProductId productId, ProductOwnerId productOwnerId, ssortingng name, ssortingng description) { State = new ProductState(); State.ProductKey = tenantId.Id + ":" + productId.Id; State.ProductOwnerId = productOwnerId; State.Name = name; State.Description = description; State.BacklogItems = new List(); } internal Product(ProductState state) { State = state; } //... private readonly ProductState State; } public class ProductState { [Key] public ssortingng ProductKey { get; set; } public ProductOwnerId ProductOwnerId { get; set; } public ssortingng Name { get; set; } public ssortingng Description { get; set; } public List BacklogItems { get; set; } ... } 

Le référentiel utiliserait un constructeur interne pour instancier (charger) une instance d’entité à partir de sa version persistante dans la firebase database.

Le seul élément que je peux append moi-même est que probablement l’object de domaine Product doit être recouvert d’un autre accesseur, uniquement dans un but de persistance via EF. le chemin inverse devrait être autorisé par quelque chose comme:

 public class Product { // ... internal ProductState State { get { // return this.State as is, if you trust the caller (repository), // or deep clone it and return it } } } // inside repository.Add(Product product): dbContext.Add(product.State); 

Pour AssignedOffers: si vous examinez le code, vous verrez que AssignedOffers renvoie la valeur d’un champ. NHibernate peut remplir ce champ comme suit: Map (x => x.AssignedOffers) .Access.Field ().

D’accord avec l’utilisation de CQS.

En faisant DDD à la première étape, vous ignorez les problèmes de persistance. L’ORM étant étroitement couplé à un SGBDR, il s’agit d’un problème de persistance.

Un ORM modélise la structure de persistance PAS le domaine. Fondamentalement, le référentiel doit “convertir” la racine agrégée reçue en une ou plusieurs entités de persistance. Le contexte délimité compte beaucoup puisque la racine globale change en fonction de ce que vous essayez d’accomplir.

Supposons que vous souhaitiez enregistrer le membre dans le contexte d’une nouvelle offre atsortingbuée. Ensuite, vous aurez quelque chose comme ça (bien sûr, ce n’est qu’un scénario possible)

 public interface IAssignOffer { int OwnerId {get;} Offer AssignOffer(OfferType offerType, IOfferValueCalc valueCalc); IEnumerable NewOffers {get; } } public class Member:IAssignOffer { /* implementation */ } public interface IDomainRepository { void Save(IAssignOffer member); } 

Ensuite, le référentiel n’obtiendra que les données nécessaires pour modifier les entités NH et c’est tout.

À propos de EVent Sourcing, je pense qu’il faut voir si cela convient à votre domaine et je ne vois pas de problème à utiliser Event Sourcing uniquement pour stocker le domaine Ragres agrégés, le rest (principalement des infrastructures) pouvant être stocké de manière ordinaire (relationnel). les tables). Je pense que CQRS vous donne une grande flexibilité dans ce domaine.