Objets métier dans une couche d'accès aux données

12

J'ai donc créé une couche d'accès aux données via TDD et j'ai abordé une certaine préoccupation. Je préfère ne pas m'engager dans la mauvaise voie, alors j'ai pensé que je vous demanderais de voir si mes pensées étaient conformes à une architecture propre.

Les méthodes de ma couche d'accès aux données (DAL pour faire court) sont assez simples. Ils sont en ligne avec les procédures stockées dans la base de données (aucun autre moyen de l'appeler pour garder les choses propres), et ils contiennent les mêmes paramètres que les procédures. Ils se connectent ensuite à la base de données et renvoient le résultat de la requête. Voici un exemple:

public int DeleteRecord(int recordId)
{
    recordId.RequireThat("recordId").NotZeroOrLess();

    List<SqlParameter> parameters = new List<SqlParameter>();
    parameters.Add(new SqlParameter { ParameterName = "@RecordId", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Input, Value = recordId});

    return this.ExecuteNonQuery("DeleteRecord", parameters.ToArray());
}

Cela fonctionne parfaitement pour ce type de méthode, car je ne fais rien de significatif avec l'ensemble de résultats. Je veux juste m'assurer que la commande a fonctionné, donc je vais retourner le résultat de la non-requête, qui est juste les lignes affectées, et je peux vérifier la logique en utilisant ce numéro.

Cependant, disons dans une autre méthode DAL, je veux charger un enregistrement. Ma procédure de chargement va être exécutée selectssur un tas de tables et renvoyer un DataSet, mais je me demande si mon DAL doit créer les objets métier dans la méthode en utilisant le DataSet, ou si mes objets métier eux-mêmes devraient simplement avoir une Load()méthode qui obtient le DataSetdu DAL, puis se remplit essentiellement.

Le faire via le DAL entraînerait moins de logique dans les objets métier (même s'il ne s'agit que d'une logique sélectionnée, c'est toujours de la logique), mais surchargerait un peu le DAL et donnerait l'impression qu'il fait vraiment quelque chose qu'il ne devrait pas '' t faire.

Qu'en pensez-vous?


la source
Pourquoi n'avez-vous pas utilisé Entity Framework?
jfrankcarr
@jfrankcarr - Pour être honnête, principalement parce que je ne le connais pas aussi bien que je le devrais. Cependant, je devrais retravailler mes tables et ajouter les clés étrangères appropriées, etc. pour que Entity Framework reconnaisse correctement les relations. Par simple curiosité cependant, si je l'utilisais, est-ce que je ferais tout le choix en utilisant le framework avec les Business Objects eux-mêmes, ou y aurait-il encore une décision quant à l'endroit où placer ces requêtes LINQ?
Je recommanderais de prendre le temps d'apprendre EF. Cela peut sembler un peu intimidant au premier abord, surtout lorsque vous essayez de l'adapter à une base de données existante qui présente des problèmes de conception préexistants, mais cela en vaut la peine.
jfrankcarr
Vous pouvez également consulter NHibernate si vous souhaitez étudier une autre option.
Don 01001100
@jfrankcarr - Je vais certainement l'examiner, mais comment s'intègre-t-il avec une application d'accès aux données à plusieurs niveaux? Le cadre d'entité lui-même serait-il implémenté dans le DAL lui-même, ou dans une autre couche ou même les objets métier eux-mêmes?

Réponses:

4

Votre DAL doit renvoyer vos objets de données

Idéalement, votre DAL devrait être un objet "boîte noire", que votre code d'application peut utiliser pour demander un objet de données ou manipuler des objets de données existants. Parfois, il existe une autre couche placée entre le DAL et le code d'application appelé le Repository, qui sépare davantage les deux couches, bien que ce ne soit pas toujours nécessaire.

En outre, vous ne souhaitez généralement pas que vos objets métier puissent se créer eux-mêmes. Cela peut provoquer des failles de sécurité où quelqu'un peut utiliser votre bibliothèque et créer une nouvelle instance de votre objet en l'appelant .Load(someId), et cela fusionne deux couches qui devraient être complètement séparées.

Je ne recommande pas non plus de fournir une .Load(DataSet ds)méthode, car si la définition de l'ensemble de données change, vous devrez rechercher les objets de données qui utilisent cet ensemble de données et les modifier. Il est plus facile de conserver tous vos codes d'accès aux données en un seul endroit, donc si vous modifiez la requête d'accès aux données, vous ne devriez avoir qu'à changer votre couche DAL.

Rachel
la source
Je ne sais pas comment on peut dépendre d'un calque pour "retourner le bon objet" si la définition de "bon objet" est conservée dans un autre calque.
TMN
@TMN C'était mal formulé. J'ai un peu changé le libellé parce que vous avez raison, le code de l'application devrait savoir quel type d'objet il demande.
Rachel
@Rachel - Gotcha. Vous recommanderiez donc que le DAL renvoie une instance de ce que serait mon objet métier lui-même, n'est-ce pas? J'étais un peu confus par votre formulation des «objets de données», mais je pense que je le comprends. De cette façon, mon code pourrait demander un objet métier de partout où il en a besoin (pas à travers eux-mêmes), en appelant simplement BusinessObject bo = DAL.LoadRecord(id);- sound right? La logique pour mapper la requête au BO lui-même serait contenue dans le DAL, et seulement là.
1
@Scott C'est exact, même si je nommerais la méthode DAL quelque chose comme Getau lieu de Load, commeCustomer c = DAL.GetCustomer(id);
Rachel
2

Ma méthode, même avant LINQ-To-SQL et Entity Framework, consistait à avoir une interface et une bibliothèque de classes abstraites qui fournissaient un "contrat écrit" pour la communication entre les différentes couches de l'application. Ceci est parfois appelé une ontologie , une définition d'un domaine de travail. Tout ce qui passait entre les couches utilisait ce «contrat».

Je n'aime pas l'idée de passer des objets de jeu de données bruts de la couche de données à la couche métier. J'ai vu ce résultat dans un certain nombre de problèmes, en particulier lors de l'intégration de sources de données héritées. Il peut également être très difficile pour les nouvelles personnes entrant dans un projet de comprendre d'où proviennent les données. Enfin, cela nécessite que votre couche métier soit chargée de gérer les données directement à partir de la base de données, ce qui peut entraîner des complications en cours de route.

L'exemple de code que vous aviez ressemble au code que j'avais avant LINQ. J'avais une classe de fonction DB commune que j'utilisais dans mes objets DAL. Les classes DAL liraient les données et les adapteraient aux objets «contrat». Les résultats scalaires, comme votre exemple de suppression, renverraient une valeur, généralement un booléen.

jfrankcarr
la source
1
"Je n'aime pas l'idée de passer des objets de jeu de données bruts de la couche de données à la couche métier." Ce. Mille fois, ça.
Joshua Smith
@jfrankcarr - Mon DAL met en œuvre une interface, et je prévois d'avoir des interfaces pour tout ce qui transfère les données d'une couche à l'autre, donc je pense que nos idées de modèles y correspondent. Donc, recommandez-vous que je modifie les méthodes qui ExecuteScalarrenvoient le résultat direct des requêtes pour renvoyer des valeurs qui ont plus de sens pour une couche de gestion, comme bool? Je pense que sinon, c'est une réponse très similaire à celle de Rachel.
Je renvoie généralement un booléen pour les appels de création / mise à jour / suppression, sauf si j'ai besoin du nombre d'enregistrements concernés. Par exemple, je pourrais retourner un int si un proc stocké traite plusieurs lignes de commande ou quelque chose comme ça.
jfrankcarr
0

Votre DAL doit renvoyer un ensemble de données. Cet ensemble de données renvoyé doit être l'objet commercial, il ne doit y avoir rien d'autre à faire que de vérifier qu'il contient les données attendues. Si vous devez en faire plus, vous essayez d'en faire trop dans une seule procédure stockée ou de ne pas renvoyer correctement les données dans la procédure stockée.

Ryathal
la source
0

Je recommanderais à vos objets métier d'avoir un constructeur pour se remplir à partir d'un ensemble de résultats. Cela supprime le couplage entre votre DAL et la couche métier. Si vous souhaitez isoler complètement les deux, créez une mappe simple de paires nom de colonne => valeur à partir de votre jeu de résultats et passez-la au constructeur.

TMN
la source