2010-07-15 4 views
2

Est-ce une violation de la persistance igorance d'injecter une interface de dépôt dans un objet Entity Like this. En n'utilisant pas d'interface, je vois clairement un problème mais quand j'utilise une interface, y a-t-il vraiment un problème? Le code ci-dessous est-il un bon ou un mauvais modèle et pourquoi?Est-ce un anti-pattern ddd?

public class Contact 
{ 
    private readonly IAddressRepository _addressRepository; 

    public Contact(IAddressRepository addressRepository) 
    { 
     _addressRepository = addressRepository; 
    } 

    private IEnumerable<Address> _addressBook; 
    public IEnumerable<Address> AddressBook 
    { 
     get 
     { 
      if(_addressBook == null) 
      { 
       _addressBook = _addressRepository.GetAddresses(this.Id); 
      } 
      return _addressBook; 
     } 
    } 
} 

Répondre

1

Ce n'est pas vraiment une bonne idée, mais cela peut convenir à certains scénarios limités. Je suis un peu confus par votre modèle, car j'ai du mal à croire que l'adresse est votre racine agrégée, et donc il ne serait pas ordinaire d'avoir un référentiel d'adresses à part entière. Basé sur votre exemple, vous utilisez probablement une passerelle de données de table ou dao plutôt qu'un référentiel.

Je préfère utiliser un mappeur de données pour résoudre ce problème (une solution ORM ou une solution similaire). Fondamentalement, je tirerais parti de mon ORM pour traiter le carnet d'adresses comme une propriété paresseuse de la racine agrégée, "Contact". Cela présente l'avantage que vos modifications peuvent être enregistrées tant que l'entité est liée à une session.

Si je n'utilisais pas un ORM, je préférerais que l'implémentation concrète du référentiel Contact définisse la propriété du magasin de sauvegarde AddressBook (liste, ou autre). Il se peut que le référentiel définisse cette énumération sur un objet proxy qui connaît l'autre magasin de données et le charge à la demande.

+0

Le code est un exemple pur et ne repose sur aucun code réel, mais je suis d'accord le AddressRepository était un mauvais exemple – terjetyl

+0

@JasonTrue, Qu'est-ce qui ne va pas avec une adresse étant une racine agrégée? Si plusieurs contacts partageaient la même adresse (par exemple, les contacts de la même société qui n'ont pas la propriété des adresses), comment modéliseriez-vous le modèle? Ils ne pourraient pas être des VO s'ils ont une identité unique. – smartcaveman

0

Vous pouvez injecter la fonction de charge depuis l'extérieur. La nouvelle est très pratique pour Lazy<T> type dans .NET 4.0 que:

public Contact(Lazy<IEnumerable<Address>> addressBook) 
    { 
     _addressBook = addressBook; 
    } 

    private Lazy<IEnumerable<Address>> _addressBook; 
    public IEnumerable<Address> AddressBook 
    { 
     get { return this._addressBook.Value; } 
    } 

Notez également que IEnumerable<T> s pourraient être intrinsèquement paresseux de toute façon quand vous les obtenez d'un fournisseur de requête. Mais pour tout autre type, vous pouvez utiliser le Lazy<T>.

0

Normalement, lorsque vous suivez DDD vous travaillez toujours avec l'agrégat entier. Le référentiel vous renvoie toujours une racine agrégée entièrement chargée.

Cela n'a pas beaucoup de sens (dans DDD au moins) d'écrire du code comme dans votre exemple. Un agrégat Contact contiendra toujours toutes les adresses (s'il en a besoin pour son comportement, ce dont je doute pour être honnête). Ainsi, ContactRepository suppose généralement que vous construisiez tout l'agrégat de Contact où Address est une entité ou, très probablement, un objet de valeur dans cet agrégat. Parce que Address est un objet entité/valeur qui appartient à (et donc géré par) agrégat de contacts, il n'aura pas son propre référentiel car vous n'êtes pas supposé gérer les entités qui appartiennent à un agrégat en dehors de cet agrégat. CV: charge toujours tout le contact et appelle sa méthode de comportement pour faire quelque chose avec son état.

0

Depuis 2 ans que j'ai posé la question et la question quelque peu mal comprise, je vais essayer d'y répondre moi-même.

Question reformulée: "Les classes d'entités métier doivent-elles être complètement ignorantes?" Je pense que les classes d'entités devraient être totalement ignorantes en matière de persistance, car vous les instanciez à de nombreux endroits dans votre base de code, ce qui rendra rapidement obsolète la classe Repository dans le constructeur d'entités. . Cela devient encore plus évident si vous avez besoin d'injecter plusieurs dépôts.Par conséquent, j'utilise toujours une classe de gestionnaire/service distincte pour effectuer les tâches de persistance pour les entités. Ces classes sont instanciées beaucoup moins fréquemment et vous avez généralement plus de contrôle sur où et quand cela se produit. Les classes d'entités sont aussi légères que possible.

J'ai maintenant toujours 1 racine d'agrégat pr de Repository et si j'ai besoin d'une logique métier supplémentaire lorsque les entités sont récupérées à partir de référentiels, je crée généralement 1 ServiceClass pour la racine agrégée.

En prenant un exemple pincé du code dans la question comme il était un mauvais exemple que je le ferais comme ça maintenant:

Au lieu de:

public class Contact 
{ 
    private readonly IContactRepository _contactRepository; 

    public Contact(IContactRepository contactRepository) 
    { 
     _contactRepository = contactRepository; 
    } 

    public void Save() 
    { 
     _contactRepository.Save(this); 
    } 
} 

je le fais comme ceci: