DDD - la règle selon laquelle les entités ne peuvent pas accéder directement aux dépôts

dans le domaine Driven Design, il semble y avoir lots de accord que les entités ne devraient pas accéder aux dépôts directement.

est - ce que cela vient D'Eric Evans Domain Driven Design book, ou est-ce qu'il vient d'ailleurs?

où y a-t-il de bonnes explications pour le raisonnement derrière cela?

edit: pour clarifier: Je ne parle pas du Classique Oo pratique de séparer l'accès aux données en une couche séparée de la logique commerciale - je parle de l'arrangement spécifique selon lequel dans DDD, les entités ne sont pas supposées parler à la couche d'accès aux données du tout (c.-à-d. qu'elles ne sont pas supposées détenir des références aux objets du dépôt)

mise à jour: J'ai donné la prime à BacceSR parce que sa réponse semblait la plus proche, mais je suis encore assez dans le noir à ce sujet. Si c'est un principe important, il devrait y avoir un bon les articles à ce sujet en ligne quelque part, sûrement?

mise à jour: Mars 2013, les upvotes sur la question impliquent qu'il ya beaucoup d'intérêt dans ce, et même s'il ya eu beaucoup de réponses, je pense toujours qu'il ya place pour plus si les gens ont des idées à ce sujet.

134
demandé sur Community 2011-04-17 19:02:02

11 réponses

il y a un peu de confusion ici. Les dépôts ont accès aux racines agrégées. Les racines agrégées sont des entités. La raison en est la séparation des préoccupations et une bonne stratification. Cela n'a pas de sens pour les petits projets, mais si vous faites partie d'une grande équipe, vous voulez dire: "vous accédez à un produit par le biais du dépôt de produits. Le produit est une racine agrégée pour un ensemble d'entités, y compris L'objet ProductCatalog. Si vous voulez mettre à jour le ProductCatalog vous devez passer par le ProductRepository."

de cette façon, vous avez une séparation très, très claire sur la logique d'affaires et où les choses sont mises à jour. Vous n'avez pas un enfant qui est hors par lui-même et écrit ce programme entier qui fait toutes ces choses compliquées au catalogue de produits et quand il s'agit de l'intégrer au projet en amont, vous êtes assis là à le regarder et réaliser que tout doit être abandonné. Cela signifie également que lorsque les gens rejoignent l'équipe, ajouter de nouvelles fonctionnalités, ils savent où aller et comment structurer le programme.

mais attendez! Le dépôt se réfère également à la couche de persistance, comme dans le modèle de dépôt. Dans un monde meilleur, le référentiel D'Eric Evans et le modèle de référentiel auraient des noms séparés, parce qu'ils ont tendance à se chevaucher assez. Pour obtenir le modèle de dépôt vous avez contraste avec d'autres façons dont les données sont accessibles, avec un bus de service ou un système de modèle d'événement. Généralement, quand vous arrivez à ce niveau, le dépôt D'Eric Evans la définition va de côté et vous commencez à parler d'un contexte limité. Chaque contexte borné est essentiellement sa propre application. Vous pourriez avoir un système d'approbation sophistiqué pour obtenir des choses dans le catalogue de produit. Dans votre conception originale, le produit était la pièce centrale, mais dans ce contexte limité, le catalogue de produits est. Vous pouvez toujours accéder aux informations du produit et mettre à jour le produit via un bus de service, mais vous devez vous rendre compte qu'un catalogue de produits en dehors du contexte limité pourrait dire quelque chose de complètement différent.

retournez à votre question initiale. Si vous accédez à un dépôt à partir d'une entité, cela signifie que l'entité n'est pas vraiment une entité commerciale, mais probablement quelque chose qui devrait exister dans une couche de service. Cela est dû au fait que les entités sont des objets d'affaires et doivent se préoccuper d'être aussi proches que possible d'un DSL (langage spécifique au domaine). Vous n'avez que des informations commerciales dans cette couche. Si vous êtes en train de résoudre une performance question, vous saurez à regarder ailleurs puisque seulement l'information d'affaires devrait être ici. Si tout à coup, vous avez des problèmes d'application ici, vous rendez très difficile d'étendre et de maintenir une application, qui est vraiment le cœur de DDD: faire du logiciel maintenable.

réponse au Commentaire 1 : bonne question. Donc pas toute la validation se produit dans la couche domaine. Sharp a un attribut "DomainSignature" qui fait ce que vous vouloir. Il est conscient de la persistance, mais être un attribut garde la couche domaine propre. Il garantit que vous n'avez pas une entité dupliquée avec, dans votre exemple, le même nom.

mais parlons de règles de validation plus compliquées. Disons que vous êtes Amazon.com. Avez-vous déjà commandé quelque chose avec une carte de crédit périmée? J'ai, où je n'ai pas mis à jour la carte et acheté quelque chose. Il accepte la commande et L'interface utilisateur m'informe que tout est parfait. Environ 15 minutes plus tard, je recevrai un e-mail disant qu'il y a un problème avec ma commande, ma carte de crédit est invalide. Ce qui se passe ici, c'est que, idéalement, il y a une validation regex dans la couche domaine. Est-ce un bon numéro de carte de crédit? Si oui, persistent l'ordre. Cependant, il y a une validation supplémentaire à la couche des tâches d'application, où un service externe est interrogé pour voir si le paiement peut être fait sur la carte de crédit. Dans la négative, n'expédiez rien, suspendez la commande et attendez le client. Ce tout doit se faire dans une couche de service.

n'ayez pas peur de créer des objets de validation à la couche service que peut accéder aux dépôts. Il suffit de le garder hors de la couche domaine.

38
répondu kertosis 2012-07-05 16:35:18

c'est une très bonne question. J'attends avec impatience une discussion à ce sujet. Mais je pense que c'est mentionné dans plusieurs livres DDD et Jimmy Nilsson et Eric Evans. Je suppose que c'est aussi visible à travers des exemples comment utiliser le motif de Repository.

mais discutons. Je pense qu'une pensée très valable est pourquoi une entité devrait savoir comment persister une autre entité? Il est Important pour la DDD que chaque entité ait la responsabilité de gérer ses propres "connaissance-sphère" et ne devrait pas savoir quoi que ce soit sur la façon de lire ou d'écrire d'autres entités. Bien sûr, vous pouvez probablement juste ajouter une interface de dépôt à L'Entity A pour lire les Entities B. Mais le risque est que vous exposez les connaissances pour savoir comment persister B. l'entity A fera-t-elle aussi la validation sur B avant de persister B dans db?

comme vous pouvez le voir, l'entité a peut s'impliquer davantage dans le cycle de vie de l'entité B et cela peut ajouter plus de complexité au modèle.

je suppose (sans exemple) que les tests unitaires seront plus complexes.

mais je suis sûr qu'il y aura toujours des scénarios où vous serez tenté d'utiliser des dépôts via des entités. Vous devez examiner chaque scénario pour porter un jugement valable. Des avantages et des Inconvénients. Mais la solution de référentiel-entité à mon avis commence avec beaucoup de contre. Ce doit être un scénario très spécial avec des avantages qui équilibrent les inconvénients....

23
répondu Magnus Backeus 2011-09-15 06:32:39

Au début, j'ai été de la persuasion pour permettre à certains de mes entités d'accès aux référentiels (ie. chargement paresseux sans ORM). Plus tard, je suis arrivé à la conclusion que je ne devrais pas et que je pouvais trouver d'autres moyens:

  1. nous devrions connaître nos intentions dans une requête et ce que nous voulons du domaine, donc nous pouvons faire des appels de dépôt avant de construire ou d'invoquer le comportement agrégé. Cela permet également d'éviter le problème de l'incohérence de l'état de mémoire et la nécessité d'un chargement paresseux (voir cet "article ). L'odeur est que vous ne pouvez plus créer une instance en mémoire de votre entité sans vous soucier de l'accès aux données.
  2. CQS (commande de séparation des requêtes) peut aider à réduire le besoin de vouloir appeler le dépôt pour des choses dans nos entités.
  3. nous pouvons utiliser un spécification pour encapsuler et communiquer les besoins de logique de domaine et passer que pour le dépôt au lieu de cela (un service peut orchestrer ces choses pour nous). La spécification peut provenir de l'entité qui est chargée de maintenir cet invariant. Le dépôt interprétera des parties de la spécification dans sa propre implémentation de requête et appliquera les règles de la spécification sur les résultats de requête. Ceci vise à maintenir la logique du domaine dans la couche du domaine. Il sert aussi mieux la langue omniprésente et la communication. Imaginez dire "spécification de commande en retard" au lieu de dire " ordre de filtre de tbl_order où placed_at est à moins de 30 minutes avant de sysdate" (voir cette réponse ).
  4. il rend le raisonnement sur le comportement des entités plus difficile puisque le principe de la responsabilité unique est violé. Si vous avez besoin de résoudre les problèmes de stockage/persistance, vous savez où aller et où ne pas aller.
  5. il évite le danger de donner à une entité un accès bidirectionnel à l'état global (via les services de dépôt et de domaine). Vous ne voulez pas non plus briser les limites de votre transaction.

Vernon Vaughn dans le livre rouge Implementing Domain-Driven Design se réfère à cette question dans deux endroits que je connais (note: ce livre est entièrement approuvé par Evans comme vous pouvez le lire dans l'avant-propos). Dans le chapitre 7 sur les Services, il utilise un service de domaine et une spécification pour contourner le besoin d'un agrégat d'utiliser un dépôt et un autre agrégat pour déterminer si un utilisateur est authentifier. Il aurait dit:

en règle générale, nous devrions essayer d'éviter l'utilisation de Référentiels (12) à l'intérieur des Agrégats, si possible.

Vernon, Vaughn (2013-02-06). Mise En Œuvre De La Conception Axée Sur Le Domaine (Kindle Location 6089). Pearson Education. L'Édition Kindle.

et au chapitre 10 sur les agrégats, dans la section intitulée" modèle Navigation " dit-il (juste après qu'il recommande l'utilisation de l'IDs global unique pour référencer d'autres racines agrégées):

la référence par identité n'empêche pas complètement la navigation à travers modèle. Certains utiliseront un dépôt (12) à partir de L'intérieur D'un agrégat pour la recherche. Cette technique est appelée modèle de domaine déconnecté, et c'est en fait une forme de chargement paresseux. Il y a un autre recommandé approche, cependant: utiliser un référentiel ou Domaine de Service (7) afin de trouver la objets dépendants avant d'invoquer le comportement global. Client Le service d'Application peut le contrôler, puis l'expédier à L'ensemble:

il va montrer un exemple de ce dans le code:

public class ProductBacklogItemService ... { 

   ... 
   @Transactional 
   public void assignTeamMemberToTask( 
        String aTenantId, 
        String aBacklogItemId, 
        String aTaskId, 
        String aTeamMemberId) { 

        BacklogItem backlogItem = backlogItemRepository.backlogItemOfId( 
                                        new TenantId( aTenantId), 
                                        new BacklogItemId( aBacklogItemId)); 

        Team ofTeam = teamRepository.teamOfId( 
                                  backlogItem.tenantId(), 
                                  backlogItem.teamId());

        backlogItem.assignTeamMemberToTask( 
                  new TeamMemberId( aTeamMemberId), 
                  ofTeam,
                  new TaskId( aTaskId));
   } 
   ...
}     

il poursuit en mentionnant également une autre solution de la façon dont un service de domaine peut être utilisé dans une méthode de commande agrégée avec Double-expédition . (Je ne peux pas le recommander ça suffit comme ça pour lire son livre. Après que vous avez fatigué d'end-lessly rummaging à travers l'internet, la fourchette sur l'argent bien mérité et lire le livre.)

j'ai ensuite eu quelques discussion avec le toujours gracieux Marco Pivetta @Ocramius qui m'a montré un peu de code sur l'extraction d'une spécification du domaine et en utilisant que:

1) Ceci n'est pas recommandé:

$user->mountFriends(); // <-- has a repository call inside that loads friends? 

2) Dans un service de domaine, c'est bien:

public function mountYourFriends(MountFriendsCommand $mount) { /* see http://store.steampowered.com/app/296470/ */ 
    $user = $this->users->get($mount->userId()); 
    $friends = $this->users->findBySpecification($user->getFriendsSpecification()); 
    array_map([$user, 'mount'], $friends); 
}
22
répondu prograhammer 2017-05-23 11:33:26

j'ai trouvé que ce blog avait de très bons arguments contre l'encapsulation de dépôts à L'intérieur D'entités:

http://thinkbeforecoding.com/post/2009/03/04/How-not-to-inject-services-in-entities

11
répondu ahaaman 2012-11-28 13:33:23

pourquoi séparer l'accès aux données?

à partir du livre, je pense que les deux premières pages du chapitre Model Driven Design donne une certaine justification pour la raison pour laquelle vous voulez faire abstraction des détails techniques de mise en œuvre de la mise en œuvre du modèle de domaine.

  • vous voulez garder une connexion étroite entre le modèle de domaine et le code
  • la séparation des préoccupations techniques aide à prouver que le modèle est pratique pour mise en œuvre
  • Vous voulez l'omniprésence de la langue de pénétrer à travers la conception du système

tout cela semble être dans le but d'éviter un" modèle d'analyse " séparé qui devient séparé de la mise en œuvre effective du système.

D'après ce que j'ai compris du livre, il est dit que ce" modèle d'analyse " peut finir par être conçu sans envisager la mise en œuvre du logiciel. Une fois que les développeurs essaient de mettre en œuvre le modèle compris par le côté commercial ils forment leurs propres abstractions en raison de la nécessité, provoquant un mur dans la communication et la compréhension.

dans l'autre sens, les développeurs introduisant trop de préoccupations techniques dans le modèle de domaine peuvent également causer cette fracture.

donc, vous pourriez considérer que la pratique de la séparation des préoccupations telles que la persistance peut aider à se prémunir contre ces modèles d'analyse divergents. Si c' l'estime nécessaire d'introduire des choses comme la persistance dans le modèle, alors c'est un drapeau rouge. Peut-être que le modèle n'est pas pratique pour la mise en œuvre.

citant:

" le modèle unique réduit les risques d'erreur, parce que la conception est maintenant une conséquence directe du modèle soigneusement étudié. La conception, et même le code lui-même, a la communicativité d'un modèle."

la façon dont j'interprète ceci, si vous avez fini avec plus les lignes de code traitant de choses comme l'accès aux bases de données, vous perdez cette capacité de communication.

si le besoin d'accéder à une base de données est pour des choses comme la vérification de l'unicité, jeter un oeil à:

Udi Dahan: les plus grandes erreurs des équipes lors de l'application de DDD

http://gojko.net/2010/06/11/udi-dahan-the-biggest-mistakes-teams-make-when-applying-ddd /

sous " toutes les règles ne sont pas créés égaux"

et

en utilisant le Modèle du Domaine Modèle

http://msdn.microsoft.com/en-us/magazine/ee236415.aspx#id0400119

sous" scénarios pour ne pas utiliser le modèle de domaine", qui touche au même sujet.

comment séparer l'accès aux données

chargement de données par une interface

Les "données couche d'accès" a été abstraite par une interface, que vous appelez afin de récupérer les données nécessaires:

var orderLines = OrderRepository.GetOrderLines(orderId);

foreach (var line in orderLines)
{
     total += line.Price;
}

Pros: l'interface sépare le code de plomberie "d'accès aux données", vous permettant d'écrire encore des tests. L'accès aux données peuvent être traitées au cas par cas, permettant de meilleures performances que d'une stratégie générique.

contre: le code d'appel doit supposer ce qui a été chargé et ce qui ne l'a pas été.

Say GetOrderLines renvoie les objets OrderLine avec une propriété null ProductInfo pour des raisons de performance. Le développeur doit avoir une connaissance intime du code derrière l'interface.

j'ai essayé cette méthode sur des systèmes réels. Vous finissez par changer la portée de ce qui est chargé tout le temps dans une tentative de corriger les problèmes de performance. Vous finissez par regarder derrière l'interface pour regarder le code d'accès aux données pour voir ce qui est chargé et ce qui ne l'est pas.

maintenant, la séparation des préoccupations devrait permettre au concepteur de se concentrer sur un aspect du code en même temps, autant que possible. La technique de l'interface supprime la façon dont ces données sont chargées, mais pas la quantité de données chargée, quand elle est chargée, et où elle est chargée.

Conclusion: séparation assez faible!

Lazy Loading

Les données

sont chargées à la demande. Les appels à charger des données sont cachés dans le graphique d'objet lui-même, où l'accès à un la propriété peut provoquer une requête sql à exécuter avant de retourner le résultat.

foreach (var line in order.OrderLines)
{
    total += line.Price;
}

Pros: Le "quand, où et comment" de l'accès aux données est caché du développeur se concentrant sur la logique du domaine. Il n'y a pas de code dans l'agrégat qui traite des données de chargement. La quantité de données chargées peuvent être la quantité exacte requise par le code.

inconvénients: lorsque vous êtes frappé avec un problème de performance, il est difficile de corriger lorsque vous avez un générique " Taille unique" solution. Un chargement paresseux peut causer des performances plus mauvaises dans l'ensemble, et la mise en œuvre de chargement paresseux peut être délicate.

Rôle D'Interface/Chargement Agressif

chaque cas d'utilisation est rendu explicite via une interface de rôle implémentée par la classe aggregate, permettant de gérer les stratégies de chargement de données par cas d'utilisation.

stratégies de chargement peut ressembler à ceci:

public class BillOrderFetchingStrategy : ILoadDataFor<IBillOrder, Order>
{
    Order Load(string aggregateId)
    {
        var order = new Order();

        order.Data = GetOrderLinesWithPrice(aggregateId);

        return order;
    }

}

puis votre agrégat peut ressembler à:

public class Order : IBillOrder
{
    void BillOrder(BillOrderCommand command)
    {
        foreach (var line in this.Data.OrderLines)
        {
            total += line.Price;
        }

        etc...
    }
}

la stratégie Billorderfetching est utilisée pour construire l'agrégat, puis l'agrégat fait son travail.

Pros: Permet le code personnalisé par cas d'utilisation, permettant une performance optimale. Est en ligne avec le principe de ségrégation D'Interface . Aucune exigence de code complexe. Les tests d'agrégats unitaires n'ont pas à imiter la stratégie de chargement. Une stratégie de chargement générique peut être utilisée dans la majorité des cas (par exemple, une des stratégies spéciales de chargement peuvent être mises en œuvre lorsque cela est nécessaire.

inconvénients: Le développeur doit encore ajuster/revoir la stratégie de récupération après avoir changé de code de domaine.

avec l'approche de stratégie de fetching vous pourriez encore vous retrouver à modifier le code de fetching personnalisé pour un changement dans les règles d'affaires. Ce n'est pas une séparation parfaite des préoccupations mais finira par plus maintenable et est mieux que la première option. La stratégie de séduction ne encapsuler le COMMENT, QUAND et OÙ les données sont chargées. Il a une meilleure séparation des préoccupations, sans perdre la flexibilité comme la taille unique s'adapte à toute approche de chargement paresseux.

10
répondu tomtg 2012-07-05 16:04:55

quelle excellente question. Je suis sur le même chemin de la découverte, et la plupart des réponses à travers l'internet semblent apporter autant de problèmes car ils apportent des solutions.

donc (au risque d'écrire quelque chose avec lequel je ne suis pas d'accord dans un an) voici mes découvertes jusqu'à présent.

tout D'abord, nous aimons un modèle de domaine riche , ce qui nous donne haut découverte (de ce que nous pouvons faire avec un agrégat) et lisibilité (appels de méthode expressifs).

// Entity
public class Invoice
{
    ...
    public void SetStatus(StatusCode statusCode, DateTime dateTime) { ... }
    public void CreateCreditNote(decimal amount) { ... }
    ...
}

nous voulons atteindre cet objectif sans injecter de services dans le constructeur d'une entité, parce que:

  • L'Introduction d'un nouveau comportement (qui utilise un nouveau service) pourrait conduire à un changement de constructeur, ce qui signifie que le changement affecte chaque ligne qui instancie l'entité !
  • ces Les services ne font pas partie du modèle , mais l'injection par le constructeur suggère qu'ils l'étaient.
  • souvent, un service (même son interface) est un détail d'implémentation plutôt qu'une partie du domaine. Le modèle de domaine aurait une dépendance orientée vers l'extérieur .
  • Il peut être confusion pourquoi la société ne peut exister sans ces dépendances. (Un service de carte de crédit, vous dites? Je ne vais même pas faire n'importe quoi avec des notes de crédit...)
  • il le rendrait dur instantiate, donc difficile à tester .
  • le problème se propage facilement, parce que d'autres entités contenant celui - ci obtiendraient les mêmes dépendances-qui sur elles peuvent ressembler à très dépendances non naturelles .

Comment pouvons-nous faire cela? Ma conclusion jusqu'à présent est que les dépendances de méthode et double dispatch fournissent une solution décente.

public class Invoice
{
    ...

    // Simple method injection
    public void SetStatus(IInvoiceLogger logger, StatusCode statusCode, DateTime dateTime)
    { ... }

    // Double dispatch
    public void CreateCreditNote(ICreditNoteService creditNoteService, decimal amount)
    {
        creditNoteService.CreateCreditNote(this, amount);
    }

    ...
}

CreateCreditNote() nécessite maintenant un service qui est responsable de la création de notes de crédit. Il utilise expédition double , entièrement déchargement du travail au service responsable, tandis que maintien de la découverte de l'entité Invoice .

SetStatus() a maintenant une simple dépendance sur un logger, qui évidemment effectuera partie de l'œuvre .

pour ce dernier, pour faciliter les choses sur le code client, nous pourrions plutôt se connecter à travers un IInvoiceService . Après tout, l'enregistrement des factures semble plutôt intrinsèque à une facture. Un tel IInvoiceService unique permet d'éviter le besoin de toutes sortes de mini-services pour diverses opérations. Le l'inconvénient est qu'il devient obscur ce que exactement ce service sera faire . Il pourrait même commencer à regarder comme une expédition double, alors que la plupart du travail est vraiment encore fait dans SetStatus() lui-même.

nous pourrions encore nommer le paramètre 'logger', dans l'espoir de révéler notre intention. Semble un peu faible, cependant.

au lieu de cela, j'opterais pour demander un IInvoiceLogger (comme nous le faisons déjà dans l'échantillon de code) et avoir IInvoiceService implémenter cette interface. Le code client peut simplement utiliser son unique IInvoiceService pour toutes les méthodes Invoice qui demandent un tel très particulier, facture-intrinsèque "mini-service", alors que les signatures de la méthode indiquent encore très clairement ce qu'ils demandent.

je constate que je n'ai pas adressé repositories exlicitement. Eh bien, l'enregistreur ou utilise un référentiel, mais permettez-moi aussi fournir un exemple explicite. Nous pouvons utiliser la même approche, si le dépôt n'est nécessaire qu'une méthode ou deux.

public class Invoice
{
    public IEnumerable<CreditNote> GetCreditNotes(ICreditNoteRepository repository)
    { ... }
}

en fait, cela fournit une alternative à la toujours gênante lazy loads .

mise à Jour: j'ai laissé le texte ci-dessous à des fins historiques, mais je suggère de direction claire de paresseux charge à 100%.

Pour de vrai, fondée sur la propriété paresseux charges, je faire utilisez actuellement l'injection de constructeur, mais d'une manière obstinée-ignorant.

public class Invoice
{
    // Lazy could use an interface (for contravariance if nothing else), but I digress
    public Lazy<IEnumerable<CreditNote>> CreditNotes { get; }

    // Give me something that will provide my credit notes
    public Invoice(Func<Invoice, IEnumerable<CreditNote>> lazyCreditNotes)
    {
        this.CreditNotes = new Lazy<IEnumerable<CreditNotes>>() => lazyCreditNotes(this));
    }
}

d'une part, un dépôt qui charge un Invoice à partir de la base de données peut avoir un accès libre à une fonction qui chargera les notes de crédit correspondantes, et injecter cette fonction dans le Invoice .

d'autre part, le code qui crée un réel nouveau Invoice passera simplement une fonction qui retourne un vide liste:

new Invoice(inv => new List<CreditNote>() as IEnumerable<CreditNote>)

(une coutume ILazy<out T> pourrait nous débarrasser de la distribution laide à IEnumerable , mais cela compliquerait la discussion.)

// Or just an empty IEnumerable
new Invoice(inv => IEnumerable.Empty<CreditNote>())

je serais heureux d'entendre vos opinions, préférences, et améliorations!

6
répondu Timo 2018-07-09 09:51:32

pour moi, il semble qu'il s'agisse d'une bonne pratique générale en matière de NEM plutôt que d'une pratique propre à la DDD.

les raisons auxquelles je peux penser sont:

  • séparation des préoccupations (les entités doivent être séparées de la façon dont elles sont maintenues. comme il pourrait y avoir plusieurs stratégies dans lesquelles la même entité serait persisté selon le scénario d'utilisation)
  • logiquement, les entités pourraient être vues dans un niveau inférieur au niveau quels dépôts fonctionnent. Les éléments de niveau inférieur ne devraient pas avoir de connaissances sur les éléments de niveau supérieur. Par conséquent, les entrées ne doivent pas avoir de connaissances sur les dépôts.
2
répondu user1502505 2012-07-05 23:03:08

j'ai appris à coder la programmation orientée objet avant que tout ce buzz couche séparée apparaissent, et mes premiers objets / classes ont fait la carte directement à la base de données.

finalement, j'ai ajouté une couche intermédiaire parce que j'ai dû migrer vers un autre serveur de base de données. J'ai vu / entendu parler le même scénario plusieurs fois.

je pense que séparer les données d'accès (un.k.un. "Référentiel") à partir d'une logique d'entreprise, est une de ces choses, qui ont été réinventé à plusieurs reprises, le Livre de Design piloté par domaine a fait beaucoup de "bruit".

j'utilise actuellement 3 couches (GUI, Logic, Data Access), comme le fait beaucoup de développeurs, parce que c'est une bonne technique.

séparant les données, en une couche Repository (A. K. A. Data Access ), peut être vu comme une bonne technique de programmation, pas seulement une règle, à suivre.

comme beaucoup de méthodologies, vous pouvez vouloir commencer, par pas mis en œuvre, et éventuellement, mettre à jour votre programme, une fois que vous les comprenez.

Quote: L'Iliade n'a pas été totalement inventée par Homer, Carmina Burana n'a pas été totalement inventée par Carl Orff, et dans les deux cas, la personne qui a mis les autres au travail, tous les tougheter, a obtenu le crédit; -)

1
répondu umlcat 2015-03-06 18:48:29

est-ce que cela vient d'Eric Evans Domain Driven Design book, ou est-ce qu'il vient d'ailleurs?

c'est un vieux truc. Le livre d'Eric l'a juste fait vibrer un peu plus.

où y a-t-il de bonnes explications pour le raisonnement derrière cela?

la raison est simple - l'esprit humain devient faible quand il fait face à des contextes multiples vaguement liés. Elles conduisent à des ambiguïtés (L'Amérique en Amérique du Sud/Amérique du Nord signifie Amérique du Sud/Amérique du Nord), l'ambiguïté conduit à la cartographie constante de l'information chaque fois que l'Esprit "touche" et qui se résume à une mauvaise productivité et des erreurs.

la logique commerciale doit être reflétée aussi clairement que possible. Les clés étrangères, la normalisation, la cartographie relationnelle d'objet proviennent d'un domaine complètement différent - ce sont des choses techniques, informatiques.

par analogie: si vous apprenez à écrire à la main, vous ne devriez pas être chargé de comprendre où la plume a été faite, pourquoi l'encre tient sur le papier, quand le papier a été inventé et ce qui sont d'autres célèbres inventions chinoises.

éditer: pour clarifier: Je ne parle pas de la pratique oo classique de séparer l'accès aux données dans une couche séparée de la logique d'affaires - je parle de l'arrangement spécifique selon lequel dans DDD, les entités ne sont pas censés parler à la couche d'accès aux données du tout (i.e. ils ne sont pas censés tenir les références à des objets du Référentiel)

la raison est toujours la même que j'ai mentionnée ci-dessus. Ici c'est juste un pas de plus. Pourquoi les entités devraient être partiellement ignorantes de la persistance si elles peuvent être (au moins proches) totalement? Moins de domaine-des préoccupations sans rapport Notre modèle tient - plus de respiration notre esprit obtient quand il doit re-interpréter.

0
répondu Arnis Lapsa 2012-07-09 14:05:21

simplement Vernon Vaughn donne une solution:

utilisez un service de dépôt ou de domaine pour rechercher des objets dépendants l'invocation de l'agrégat comportement. Un service de demande de service à la clientèle peut: le contrôle de cette.

0
répondu Alireza Rahmani 2017-08-21 07:28:53

dans le monde idéal , DDD propose que les entités ne devraient pas faire référence à des couches de données. mais nous ne vivons pas dans un monde idéal. Les domaines peuvent avoir besoin de se référer à d'autres objets de domaine pour la logique des affaires avec lesquels ils pourraient ne pas avoir de dépendance. Il est logique pour les entities de se référer à la couche de dépôt pour un usage en lecture seule, pour récupérer les valeurs.

-1
répondu vsingh 2013-11-07 14:04:55