Question

J'ai un modèle riche de domaine, où la plupart des classes ont un comportement et des propriétés qui sont soit calculées ou exposer les propriétés des objets membres (ce qui est de dire que les valeurs de ces propriétés ne sont jamais persisté).

Mon client parle au serveur uniquement via WCF.

En tant que tel, pour chaque entité de domaine, j'ai un DTO correspondant - une représentation simple qui ne contient que des données - ainsi qu'une classe mappeur qui implémente DtoMapper<DTO,Entity> et peut convertir une entité à son équivalent DTO ou vice-versa par une passerelle statique:

var employee = Map<Employee>.from_dto<EmployeeDto>();

Le côté serveur de cette application est surtout sur la persistance, où mes DTO viennent du service WCF, sont désérialisée, puis un ORM arbitraire les persiste à la base de données, ou une demande de requête vient de WCF et les exécute ORM que requête sur la base de données et renvoie les objets à sérialiser et renvoyés par WCF.

Compte tenu de ce scénario, est-il un sens à la carte mon magasin de persistance des entités de domaine, ou devrais-je simplement la carte directement aux DTO?

Si j'utilise des entités de domaine, le débit serait

  1. demandes de client objet
  2. WCF transmet la demande au serveur
  3. ORM base de données des requêtes et retourne entités de domaine
  4. entités de domaine transformées en DTO par mappeur
  5. WCF sérialise DTO et retourne au client
  6. client désérialise DTO
  7. DTO transformé en entité de domaine par mappeur
  8. viewmodels créé, etc.

similaire sur le voyage de retour

Si je la carte directement à DTO, je peux éliminer un mappage par objet, par demande. Qu'est-ce que je perds en faisant cela?

La seule chose qui vient à l'esprit est une autre occasion de valider avant insertion / mise à jour, parce que je n'ai aucune garantie que le DTO n'a jamais été soumis à la validation ou même existé comme une entité de domaine avant d'être envoyé à travers le fil, et je pense que une chance de valider sur certains (si un autre processus aurait pu mettre des valeurs non valides dans la base de données). Y at-il d'autres raisons? Sont ces raisons suffisantes pour justifier les étapes cartographiques supplémentaires?

modifier:

Je l'ai dit « arbitraire ORM » ci-dessus, et je ne veux que les choses soient comme ORM et persistance agnostique possible, mais si vous avez quelque chose de spécial à ajouter qui est spécifique à NHibernate, par tous les moyens faire.

Était-ce utile?

La solution

Je recommande personnellement de garder votre carte sur le côté serveur. Vous avez probablement fait beaucoup de travaux de construction de votre conception au point où elle est à présent; ne pas jeter que loin.

Pensez à ce service Web est. Il est non seulement une abstraction sur votre ORM; il est un contrat . Il est une API publique pour vos clients, à la fois internes et externes.

Une API publique devrait avoir peu ou pas de raison de changer. Presque tout changement à une API, en dehors de l'ajout de nouveaux types et méthodes, est un changement de rupture. Mais votre modèle de domaine ne va pas être si stricte. Vous devrez changer de temps en temps que vous ajoutez de nouvelles fonctionnalités ou de découvrir des défauts dans la conception originale. Vous voulez être en mesure d'assurer que les changements à votre modèle interne ne provoquent pas en cascade changements par le contrat de service.

Il est en fait une pratique courante (je n'insulter les lecteurs avec l'expression « meilleures pratiques ») pour créer des classes spécifiques de Request et Response pour chaque message pour une raison similaire; il devient beaucoup plus simple d'étendre la capacité des services et des méthodes existantes sans les changements étant rupture.

Les clients probablement ne sont pas veulent exactement le même modèle que vous utilisez en interne dans le service. Si vous êtes votre seul client, alors peut-être cela semble transparent, mais si vous avez des clients externes et vous avez vu à quel point de leur interprétation de votre système peut souvent être, alors vous comprendrez la valeur de ne pas permettre à votre modèle parfait de fuite les limites de l'API du service.


Et parfois, il est même pas possible pour envoyer votre modèle à travers l'API. Il y a plusieurs raisons pour lesquelles cela peut se produire:

  • cycles dans le graphe d'objet. Parfaitement bien en POO; désastreuse sérialisation. Vous finissez par avoir à faire des choix permanents douloureux dont « direction » le graphique doit être publié en feuilleton dans. Par contre, si vous utilisez un DTO, vous pouvez sérialisation dans la direction où vous voulez, ce qui convient à la tâche à accomplir.

  • Tenter d'utiliser certains types de mécanismes d'héritage sur SOAP / REST peut être une bidouille au mieux. L'ancienne sérialiseur XML au moins supporte xs:choice; DataContract ne fonctionne pas et je ne vais pas ergoter sur justification, mais il suffit de dire que vous avez probablement un polymorphisme dans votre modèle riche de domaine et il est presque impossible de canaliser que, grâce au service Web.

  • chargement Lazy / différé, que vous faites probablement usage de si vous utilisez un ORM. Il suffit en vous assurant qu'il se sérialisé bien maladroit - par exemple, en utilisant Linq à des entités SQL, WCF ne déclenche même pas le chargeur paresseux, il vous reste plus qu'à mettre null dans ce domaine, sauf si vous chargez manuellement - mais le problème est encore pire pour les données à revenir dans quelque chose aussi simple comme auto-propriété List<T> qui est initialisé dans le constructeur -. assez courant dans un modèle de domaine - ne fonctionne tout simplement pas dans WCF, car il n'invoque pas votre constructeur. Au lieu de cela, vous devez ajouter une méthode d'initialisation de [OnDeserializing], et vous vraiment ne veulent pas encombrer votre modèle de domaine avec ces ordures.

  • Je viens aussi remarqué la remarque que vous utilisez parenthétique NHibernate. Considérez que les interfaces comme IList<T> ne peuvent pas être sérialisés du tout sur un service Web! Si vous utilisez des classes POCO avec NHibernate, comme la plupart d'entre nous, cela simplement ne fonctionnera pas, période.


Il y aura aussi probablement de nombreux cas où votre modèle de domaine interne ne correspond pas simplement aux besoins du client, et il n'a pas de sens de changer votre modèle de domaine pour répondre à ces besoins. À titre d'exemple, nous allons prendre quelque chose d'aussi simple que d'une facture. Il doit montrer:

  • Informations sur le compte (numéro de compte, nom, etc.)
  • données spécifiques à la facture (numéro de facture, la date, la date d'échéance, etc.)
  • Informations A / niveau R (solde précédent, les frais de retard, nouvel équilibre)
  • Information sur le produit ou service pour tout sur la facture;
  • Etc.

Cela correspond probablement bien dans un modèle de domaine. Mais si le client veut exécuter un rapport qui montre 1200 de ces factures? Une sorte de rapport de la réconciliation?

Cette suce pour la sérialisation. Maintenant, vous envoyez 1200 factures avec les mêmes données sérialisés encore et encore - mêmes comptes, les mêmes produits, même A / R. En interne, l'application est de garder une trace de tous les liens; il connaît la facture n ° 35 et la facture n ° 45 sont pour le même client et donc partager une référence Customer; toutes ces informations sont perdues lors de la sérialisation et vous finissez par l'envoi d'une quantité ridicule de données redondantes.

Qu'est-ce que vous voulez vraiment est d'envoyer un rapport personnalisé qui comprend:

  • Tous les comptes inclus dans le rapport, et leur A / R;
  • Tous les produits inclus dans le rapport;
  • Toutes les factures, avec les ID produit et compte uniquement.

Vous devez effectuer une « normalisation » supplémentaire sur vos données sortantes avant de l'envoyer au client si vous voulez éviter la redondance massif. Cela favorise fortement l'approche DTO; il n'a pas de sens d'avoir cette structure dans votre modèle de domaine parce que votre modèle de domaine déjà prend en charge des licenciements, à sa manière.

J'espère que ce sont des exemples assez et raison d'être assez pour vous convaincre de garder vos applications de domaine <-> Contrat de service intact. Vous avez fait tout à fait la bonne chose à ce jour, vous avez un grand dessein, et il serait dommage de nier tous ces efforts en faveur de quelque chose qui pourrait conduire à des maux de tête importants plus tard.

Autres conseils

Vous devez mapper les DTO du côté client de toute façon, donc, pour la symétrie, il est préférable de faire la cartographie inverse du côté du serveur. De cette façon, vous isoler vos conversions en couches d'abstraction bien séparées.

couches d'abstraction sont bonnes non seulement pour les validations, mais pour isoler votre code de modifications ci-dessous / au-dessus, et rendre votre code plus testables et avec moins de répétitions.

En outre, à moins que vous remarquez un goulot d'étranglement dans la conversion supplémentaire, rappelez-vous: début d'optimisation est la racine de tous les maux. :)

Vous devriez vraiment garder vos entités de domaine séparé de vos années DTO ils sont différentes préoccupations. de DTO sont généralement heriachal, les modèles autodescriptifs où que vos entités de domaine d'autre part encapsulent votre logique métier et ont beaucoup de comportement attaché avec eux.

Cela dit, je ne sais pas où le mappage supplémentaire est? Vous récupérez les données à l'aide de votre ORM (aka entités de domaine) et vous associez ces objets à votre DTO donc il n'y a que 1 cartographie là? BTW si vous n'êtes pas déjà utiliser quelque chose comme Automapper pour faire la mise en correspondance pénible pour vous.

Ces mêmes DTO sont ensuite désérialisée sur le client et à partir de là, vous pouvez mapper directement à votre UIViewModels. Alors la grande image ressemble à:

  • Le client demande par entité Id du service WCF
  • WCF service devient entité de dépôt / ORM
  • Utilise un AutoMapper à la carte de l'entité à DTO
  • Le client reçoit DTO
  • Utilise un AutoMapper à la carte à l'interface utilisateur ViewModel
  • UIViewModel est binded à l'interface graphique

Quand vous dites que votre application côté serveur est « la plupart du temps » au sujet de la persistance, je pense que c'est l'élément clé pour penser. Est-il vraiment un domaine côté serveur modèle qui nécessite une certaine intelligence autour des données qu'il reçoit ou que votre service WCF agissent uniquement comme la passerelle entre votre modèle de domaine et le magasin de données?

, Voir également si votre DTO est conçu pour le domaine client.
Est-ce le seul domaine client qui a besoin d'accéder à cette banque de données via votre service?
Les DTO côté serveur assez souple ou à gros grains pour servir un domaine d'application différent?
Sinon, il vaut probablement l'effort de mise en oeuvre de garder les interfaces externes extraites.

  

(DB-> ORM-> EmployeeEntity-> Client1DTOAssembler-> Client1EmployeeDTO).

Nous avons une application similaire où un service WCF agit principalement comme une passerelle vers le magasin de données persistantes.

Dans notre cas, notre client et le serveur ne pas réutiliser l'ensemble contenant « DTO. » Cela nous donne la possibilité d'ajouter simplement le code aux classes partielles générées par la référence de service, donc nous sommes souvent en mesure d'utiliser un DTO tel quel sur le côté client et le traiter comme un objet de domaine. D'autres fois, nous pouvons avoir des objets côté client seul domaine qui servent de façades à tout un tas d'objets persistants que nous avons obtenu du service WCF.

Quand vous pensez au sujet des comportements et des propriétés calculées que vos objets de domaine ont, combien de chevauchement est là, vraiment entre votre client et le serveur? Dans notre cas, nous avons déterminé que la répartition des responsabilités entre le client et le serveur signifiait qu'il y avait très peu, le cas échéant, le code qui devait être présent (et exactement le même) sur le client et le serveur.

Pour répondre à vos questions directement, si votre objectif est de rester sur la persistance complètement agnostique, je serais certainement carte votre magasin de persistance à vos objets de domaine, puis à la carte DTO. Il y a trop de mises en œuvre de persistance qui peuvent saigner dans vos objets et compliqueraient les utiliser comme WCF DTO.

Du côté client, il peut ne pas être nécessaire de faire une cartographie supplémentaire si vous pouvez décorer ou augmenter vos DTO et c'est une solution assez simple.

Votre l'architecture semble assez bien pensé. Mon instinct sens est, si vous avez déjà décidé de réduire les objets à leur envoyer de DTO par WCF, et vous avez ne pas actuellement besoin d'une fonctionnalité d'objet supplémentaire sur le côté serveur, pourquoi ne pas garder les choses simples et carte votre magasin de persistance directement aux DTO.

Qu'est-ce que vous perdez? Je ne pense pas que vous perdez vraiment quoi que ce soit. Vous êtes l'architecture est propre et simple. Si vous décidez à l'avenir qu'il ya un nouveau besoin de fonctionnalités plus riches sur le côté serveur, vous pouvez toujours re-facteur à ce moment-là pour recréer vos entités de domaine.

Je tiens à garder les choses simples et re-facteur au besoin plus tard, essayez d'éviter la chose d'optimisation pré-mature, etc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top