Question DTO plus UnitOfWork pattern est-il une bonne approche pour concevoir un DAL pour une application Web?


Je suis en train d'implémenter une structure DAL en utilisant une entité. Sur notre application, nous avons trois couches (DAL, couche de gestion et présentation). Ceci est une application web. Lorsque nous avons commencé à mettre en œuvre la DAL, notre équipe a pensé que DAL devrait avoir des classes dont les méthodes reçoivent un ObjectContext fourni par les services de la couche de gestion et opèrent dessus. La raison de cette décision est que différents ObjectContexts voient différents états de base de données, de sorte que certaines opérations peuvent être rejetées en raison de problèmes de correspondance de clés étrangères et d'autres incohérences.

Nous avons remarqué que la génération et la propagation d'un contexte d'objet à partir de la couche de services génère un couplage élevé entre les couches. Par conséquent, nous avons décidé d'utiliser des DTO mappés par Automapper (pas d'entités non gérées ou d'entités de suivi automatique discutant un couplage élevé, exposant les entités aux couches supérieures et à faible efficacité) et UnitOfWork. Donc, voici mes questions:

  1. Est-ce la bonne approche pour concevoir le DAL d'une application Web? Pourquoi?
  2. Si vous avez répondu «oui» à 1., comment concilier le concept de DTO avec les modèles UnitOfWork?
  3. Si vous avez répondu "non" à 1., quelle pourrait être la bonne approche pour concevoir une DAL pour une application Web?

S'il vous plaît, donnez si possible une bibliographie à l'appui de votre réponse.

À propos de la conception actuelle:

L'application a été planifiée pour être développée sur trois niveaux: Présentation, Business et DAL. La couche métier a des façades et des services

Il existe une interface appelée ITransaction (avec seulement deux méthodes pour éliminer et enregistrer les modifications) uniquement visible dans les services. Pour gérer une transaction, il existe une classe Transaction qui étend un objet ObjectContext et ITransaction. Nous avons conçu cela en gardant à l'esprit qu'au niveau de la couche de gestion, nous ne souhaitons pas que d'autres méthodes ObjectContext soient accessibles.

Sur la DAL, nous avons créé un référentiel abstrait utilisant deux types génériques (l’un pour l’entité et l’autre pour son DTO associé). Ce référentiel dispose de méthodes CRUD implémentées de manière générique et de deux méthodes génériques pour mapper les DTO et les entités du référentiel générique avec AutoMapper. Le constructeur du référentiel abstrait prend un argument ITransaction et attend d'ITransaction un objet ObjectContext afin de l'affecter à sa propriété ObjectContext protégée.

Les référentiels concrets doivent uniquement recevoir et renvoyer les types .net et DTO.

Nous sommes maintenant confrontés à ce problème: la méthode générique à créer ne génère pas d’identifiant temporel ou persistant pour les entités attachées (jusqu’à ce que nous utilisions SaveChanges(), brisant ainsi la transactionnalité que nous souhaitons); cela implique que les méthodes de service ne peuvent pas l'utiliser pour associer des DTO dans le BL)


10
2017-12-29 14:34


origine


Réponses:


Il y a un certain nombre de choses qui se passent ici ... L'hypothèse que je vais faire est que vous utilisez une architecture à 3 niveaux. Cela dit, je ne suis pas certain sur quelques décisions de conception que vous avez prises et sur les motivations qui les ont motivées. En général, je dirais que votre ObjectContext ne devrait pas être passé dans vos classes. Il devrait y avoir une sorte de gestionnaire ou une classe de référentiel qui gère la gestion des connexions. Cela résout votre problème de gestion de l'état de la base de données. Je trouve qu'un modèle de référentiel fonctionne très bien ici. À partir de là, vous devriez être capable d'implémenter le modèle d'unité de travail assez facilement puisque votre gestion des connexions sera gérée au même endroit. Compte tenu de ce que je sais de votre architecture, je dirais que vous devriez utiliser une stratégie POCO. L'utilisation de POCOs ne vous lie pas étroitement à un fournisseur ORM. L'avantage est que vos POCO pourront interagir avec votre ObjectContext (probablement via un référentiel quelconque), ce qui vous donnera une visibilité sur le suivi des modifications. Encore une fois, à partir de là, vous pourrez implémenter le modèle d'unité de travail (transaction) pour vous permettre de contrôler totalement le comportement de votre transaction commerciale. Je trouve que c'est un article incroyablement utile pour expliquer comment tout cela fonctionne. Le code est bogué mais illustre avec précision les meilleures pratiques pour le type d'architecture que vous décrivez: Référentiel, spécification et mise en œuvre de l'unité de travail

La version abrégée de ma réponse à la question 1 est "non". Le lien ci-dessus fournit ce que je pense être une meilleure approche pour vous.


6
2018-01-05 16:53



J'ai toujours cru que le code peut mieux expliquer les choses que les mondes pour les programmeurs. Et cela est particulièrement vrai pour ce sujet. C'est pourquoi je vous suggère de regarder l'excellent exemple d'application dans lequel toutes les conséquences que vous attendez sont implémentées.

alt text

Le projet s'appelle Architecture pointue, il est centré autour MVC et NHibernate, mais vous pouvez utiliser les mêmes approches pour remplacer NHibernate pièces avec EF ceux quand vous en avez besoin. Le but de ce projet est de fournir un modèle d'application avec toutes les meilleures pratiques de la communauté pour la création d'applications Web.

Il couvre tous les sujets courants et les plus courants lors de l'utilisation des ORM, la gestion des transactions, la gestion des dépendances avec les conteneurs IoC, utilisation des DTO, etc.

Et voici un exemple d'application.

J'insiste pour lire et essayer ceci, ce sera un véritable trasure pour vous comme si c'était pour moi.


4
2018-01-12 10:09



Vous devriez regarder quoi injection de dépendance et inversion du contrôle en général. Cela permettrait de contrôler le cycle de vie de ObjectContext "de dehors". Vous pouvez vous assurer que seule une instance de contexte d'objet est utilisée pour chaque requête http. Pour éviter de gérer les dépendances manuellement, je vous recommande d'utiliser StructureMap comme un conteneur.

L'abstraction de la persistance est une autre technique utile (mais assez délicate et difficile à réaliser). À la place d'utiliser ObjectContext directement, vous utiliseriez ainsi Dépôt qui est responsable de fournir une collection comme API pour votre magasin de données. Cela fournit utile couture que vous pouvez utiliser pour changer le mécanisme de stockage des données sous-jacentes ou pour éliminer complètement la persistance des tests.

Comme Jason l'a déjà suggéré - Vous devriez également utiliser POCO`s (objets clr anciens). Malgré le fait qu'il y aurait toujours un couplage implicite avec une structure d'entité Vous devez être conscient que c'est bien mieux que d'utiliser des classes générées.

Les choses que vous pourriez ne pas trouver ailleurs assez vite:

  1. Essayez d'éviter l'utilisation de unité de travail. Votre modèle doit définir des limites transactionnelles.
  2. Essayez d'éviter l'utilisation de référentiels génériques (notez le point concernant IQueryable aussi).
  3. Il n'est pas obligatoire de spam Votre code avec nom du modèle de référentiel.

Aussi, vous pourriez aimer lire à propos de conception pilotée par domaine. Il aide à gérer une logique métier complexe et fournit de bonnes directives pour rendre le code moins procédural, plus orienté objet.


2
2018-01-05 17:22



Je vais me concentrer sur vos problèmes actuels: Pour être honnête, je ne pense pas que vous devriez faire le tour de votre ObjectContext. Je pense que cela va conduire à des problèmes. Je suppose qu'un contrôleur ou un service métier transmettra ObjectContext / ITransaction au référentiel. Comment vous assurerez-vous que votre ObjectContext est éliminé correctement en aval? Que se passe-t-il lorsque vous utilisez des transactions imbriquées? Qu'est-ce qui gère les retours en arrière, pour les transactions en aval?

Je pense que votre meilleur pari consiste à définir plus précisément comment vous comptez gérer les transactions dans votre architecture. Utiliser TransactionScope dans votre contrôleur / service est un bon début car ObjectContext le respecte. Bien sûr, vous devrez peut-être prendre en compte le fait que les contrôleurs / services peuvent appeler d'autres contrôleurs / services qui ont des transactions. Afin de permettre des scénarios dans lesquels vous souhaitez un contrôle total sur vos transactions commerciales et les appels de base de données ultérieurs, vous devez créer une classe TransactionManager qui enregistre et gère généralement les transactions dans votre pile. J'ai trouvé ça NCommon fait un travail extraordinaire à la fois pour la saisie et la gestion des transactions. Jetez un coup d'oeil aux classes UnitOfWorkScope et TransactionManager. Bien que je ne sois pas d'accord avec l'approche de NCommon consistant à forcer le référentiel à s'appuyer sur UnitOfWork, cela pourrait facilement être modifié si vous le souhaitiez.

En ce qui concerne votre problème persistantID, regarde ça


1
2018-01-05 21:11