l'application de base de données N-couches sans utiliser un ORM, comment l'interface utilisateur précise ce qu'il a besoin de données à afficher?

StackOverflow https://stackoverflow.com/questions/1524367

Question

Je cherche des pointeurs et des informations ici, je vais faire ce CW depuis que je soupçonne qu'il n'a pas une seule bonne réponse. Ceci est pour C #, je vais donc faire quelques références à Linq ci-dessous. Je présente également mes excuses pour le long courrier. Permettez-moi de résumer la question ici, puis toute la question suit.

Résumé: Dans une interface utilisateur / BLL / DAL / DB application 4 couches, comment les modifications de l'interface utilisateur, pour afficher plusieurs colonnes (par exemple dans une grille), éviter une fuite à travers la couche logique métier et dans l'accès aux données couche, pour mettre la main sur les données à afficher (en supposant qu'il est déjà dans la base de données).


Supposons une application en couches avec 3 (4) couches:

  • Interface utilisateur (UI)
  • couche logique métier (BLL)
  • Data Access Layer (DAL)
  • Base de données (DB, la 4ème couche)

Dans ce cas, le DAL est responsable de la construction des instructions SQL et les exécuter contre la base de données, les données de retour.

La seule façon de « correctement » construire une telle couche à le faire toujours « select * »? Pour moi, c'est un grand non-non, mais laissez-moi vous expliquer pourquoi je me demande.

Disons que je veux, pour mon interface utilisateur, pour afficher tous les employés qui ont un dossier d'emploi actif. Par « active » Je veux dire que les dossiers d'emploi de-aux dates contient aujourd'hui (ou peut-être même une date que je peux mettre dans l'interface utilisateur).

Dans ce cas, disons que je veux envoyer un e-mail à tous ces gens, donc j'ai un code dans le BLL qui assure que je ne l'ai pas déjà envoyé un courriel aux mêmes personnes déjà, etc.

Pour la BLL, il a besoin de quantités minimales de données. Peut-être qu'il appelle la couche d'accès aux données pour obtenir la liste des employés actifs, puis un appel pour obtenir une liste des e-mails qu'elle a envoyés. Ensuite, il se joint à ceux et construit une nouvelle liste. Peut-être que cela pourrait se faire avec l'aide de la couche d'accès aux données, ce n'est pas important.

Ce qui est important est que pour la couche d'affaires, il n'y a vraiment pas beaucoup de données dont il a besoin. Peut-être qu'il a juste besoin de l'identifiant unique pour chaque employé, pour les deux listes, pour correspondre à, puis dites « Ce sont les identificateurs uniques de ceux qui sont actifs, que vous ne l'avez pas déjà envoyé un courriel à ». Est-ce que je construis alors le code DAL qui construit des instructions SQL qui récupèrent seulement ce que les besoins de la couche d'affaires? C'est à dire. juste "id SELECT FROM employees WHERE ..."?

Que dois-je faire alors pour l'interface utilisateur? Pour l'utilisateur, il serait peut-être préférable d'inclure beaucoup plus d'informations, selon le pourquoi Je veux envoyer des e-mails. Par exemple, je pourrais vouloir inclure des informations de contact rudimentaire, ou le service où ils travaillent, ou leurs gestionnaires nom, etc., ne veut pas dire que je au moins le nom et à l'adresse e-mail à afficher.

Comment obtenir l'interface utilisateur que les données? Est-ce que je change la DAL pour vous assurer que je retourne assez de données retour à l'interface utilisateur? Dois-je changer la BLL pour vous assurer qu'il retourne suffisamment de données pour l'interface utilisateur? Si les structures d'objet ou de données renvoyées par le DAL retour à l'BLL peuvent être envoyés à l'interface utilisateur et, peut-être le BLL n'a pas besoin de beaucoup d'un changement, mais les exigences des impacts de l'interface utilisateur une couche au-delà de ce qu'elle devrait communiquer avec . Et si les deux mondes fonctionnent sur différentes structures de données, des changements auraient probablement à faire à la fois.

Et puis quand l'interface utilisateur est modifiée, pour aider l'utilisateur encore plus loin, en ajoutant plus de colonnes, la profondeur serait / devrais-je aller pour changer l'interface utilisateur? (En supposant que les données sont présentes dans la base de données déjà si aucun changement est nécessaire là-bas.)

Une suggestion qui a été soulevée est d'utiliser LINQ to SQL et IQueryable, de sorte que si le DAL, qui traite de ce qui (comme dans quels types de données) et pourquoi (comme dans les clauses WHERE) est revenu IQueryables, le BLL pourrait retourner ceux à l'interface utilisateur, ce qui pourrait alors construire une requête LINQ qui récupérer les données dont il a besoin. Le code d'interface utilisateur pourrait alors tirer dans les colonnes dont il a besoin. Cela work depuis avec IQuerables, l'interface utilisateur finirait par exécuter réellement la requête, et il pourrait alors utiliser « select nouveau {X, Y, Z} » pour préciser ce dont il a besoin, et même se joindre à d'autres tables, si nécessaire.

Cela semble malpropre pour moi. Que l'interface utilisateur exécute le code SQL lui-même, même si elle a été caché derrière un frontend Linq.

Mais, pour que cela se produise, le BLL ou le DAL ne doit pas être autorisé à fermer les connexions de base de données, et un type IoC du monde, le DAL-service pourrait obtenir éliminé un peu plus tôt que le code de l'interface utilisateur voudrait , de sorte que la requête Linq pourrait finir avec l'exception « ne peut pas accéder à un objet disposé ».

Je suis à la recherche pour les pointeurs. Comment sommes-nous de loin? Comment gérez-vous cela? Je considère le fait que les changements à l'interface utilisateur va fuir à travers l'BLL et dans la DAL une très mauvaise solution, mais maintenant il ne semble pas que nous pouvons faire mieux.

S'il vous plaît me dire comment nous sommes stupides et me prouver?

Et notez que c'est un système existant. Modification du schéma de base de données ne sont pas dans le champ pendant des années encore, donc une solution à utiliser des objets ORM qui essentiellement faire l'équivalent de « select * » est pas vraiment une option. Nous avons quelques grandes tables que nous aimerions éviter de tirer à travers la liste complète des couches.

Était-ce utile?

La solution

Utiliser le concept d'un modèle de vue (ou d'objets de transfert de données) qui sont des cas de consommation UI. Ce sera le travail du BLL de prendre ces objets et si les données sont incomplètes, demander des données supplémentaires (que nous appelons modèle). Ensuite, la BLL peut prendre les bonnes décisions sur ce point de vue des modèles pour revenir. Ne laissez pas votre modèle (données) spécifiques imprègnent à l'interface utilisateur.

UI <-- (viewmodel) ---> BLL <-- (model) --> Peristence/Data layers

Ce découplage permet à l'échelle votre application mieux. L'indépendance de la persistance Je pense que naturellement tombe de cette approche, la construction et la spécification des modèles de vue pourrait faire avec souplesse dans le BLL en utilisant linq2ql ou une autre technologie ORM.

Autres conseils

Ce n'est pas du tout un problème facile à résoudre. Je l'ai vu de nombreuses tentatives (y compris l'approche IQueryable vous décrivez), mais aucun de ceux qui sont parfaits. Malheureusement, nous attendons toujours la solution parfaite. Jusque-là, nous devrons faire avec l'imperfection.

Je suis complètement d'accord que les préoccupations DAL ne devraient pas être autorisés à fuir à travers les couches supérieures, donc un BLL isolant est nécessaire.

Même si vous n'avez pas le luxe de redéfinir la technologie d'accès aux données dans votre projet en cours, il contribue encore à réfléchir sur le modèle de domaine en termes de persistance ignorance. Un corrolaire de persistance L'ignorance est que chaque objet domaine est une unité autonome qui n'a aucune notion de choses comme des colonnes de base de données. Il est préférable d'appliquer integretiy de données invariants dans ces objets, mais cela signifie aussi qu'un objet de domaine instancié aura toutes ses données constitutives chargées. Il est une proposition tout ou rien, donc la clé devient de trouver un bon modèle de domaine qui garantit que chaque prise d'objets de domaine (et doit être chargé avec) un montant « approprié » des données.

Trop objets granuleux peuvent conduire à des interfaces bavard DAL, mais les objets trop gros grains peut conduire à trop de données non pertinentes en cours de chargement.

Un exercice très important est d'analyser et de modéliser correctement les agrégats de modèle de domaine afin qu'ils frappent le bon équilibre. Le livre Domain-Driven Design contient des analyses très éclairants des agrégats de modélisation.

Une autre stratégie qui peut être utile à cet égard est de viser à appliquer le principe de Hollywood, autant que possible. Le principal problème que vous décrivez concerne les requêtes, mais si vous pouvez changer votre focus à être plus orientée vers le commandement, vous pourriez être en mesure de définir des interfaces plus gros grains qui ne fonctionne pas nécessitent pour charger toujours trop de données.

Je ne suis pas au courant d'une solution simple à ce défi. Il existe des techniques comme celles décrites plus haut qui peuvent vous aider à résoudre certains des problèmes, mais à la fin il est encore un art qui exige de l'expérience, la compétence et la discipline.

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