Wie sollte der Data Access Layer strukturiert sein?

8

Ich habe mein System ursprünglich nach dem s # -Architekturbeispiel in diesem Codeprojektartikel entworfen (leider Ich verwende nicht NHibernate). Die Grundidee ist, dass für jedes Domänenobjekt, das mit der Persistenzschicht kommunizieren müsste, ein entsprechendes Datenzugriffsobjekt in einer anderen Bibliothek vorhanden wäre. Jedes Datenzugriffsobjekt implementiert eine Schnittstelle und wenn ein Domänenobjekt Zugriff auf eine Datenzugriffsmethode benötigt, codiert es immer mit einer Schnittstelle und niemals mit den DAOs selbst.

Zu der Zeit und immer noch dachte ich, dass dieses Design sehr flexibel ist. Da jedoch die Anzahl der Objekte in meinem Domänenmodell zugenommen hat, frage ich mich, ob es hier ein organisatorisches Problem gibt. Zum Beispiel endet fast jedes Objekt in der Domäne mit einer entsprechenden Datenzugriffsobjekt- und Datenzugriffsobjekt-Schnittstelle. Nicht nur das, sondern jeder einzelne davon befindet sich an einem anderen Ort, der schwieriger zu warten ist, wenn ich etwas Einfaches machen möchte, wie zum Beispiel um einige Namespaces zu verschieben.

Interessanterweise sind viele dieser DAOs (und ihre entsprechenden Schnittstellen) sehr einfache Kreaturen - am häufigsten wird nur eine einzige GetById () -Methode verwendet. Ich lande mit einer ganzen Reihe von Objekten wie

%Vor%

Wo ihre Implementierer normalerweise auch sehr trivial sind. Das lässt mich fragen, ob es nicht einfacher wäre, in eine andere Richtung zu gehen, vielleicht meine Strategie zu ändern, indem ich ein einzelnes Objekt für einfache Datenzugriffsaufgaben und die Erstellung dedizierter Datenzugriffsobjekte für diejenigen, die etwas mehr benötigen, vorbehalte kompliziert.

%Vor%

Hat jemand Erfahrung mit einer Architektur wie dieser? Insgesamt bin ich sehr zufrieden mit der Einrichtung, da es jetzt nur noch darum geht, all diese kleinen Dateien zu verwalten. Ich frage mich jedoch noch, welche anderen Ansätze zur Strukturierung der Datenzugriffsschicht existieren.

    
George Mauer 27.09.2008, 21:16
quelle

5 Antworten

3

Ich empfehle den einfachen Ansatz anders als in einfachen Systemen, normalerweise denke ich, dass es besser ist, ein benutzerdefiniertes Repository für jedes Aggregat zu erstellen und so viel geeignete Logik einzukapseln wie möglich.

Also würde mein Ansatz ein Repository für jedes Aggregat haben, das es benötigt, wie CustomerRepository. Dies hätte eine Add (save) -Methode und, falls für dieses Aggregat geeignet, eine Remove (delete) -Methode. Es hätte auch andere benutzerdefinierte Methoden, einschließlich Abfragen (GetActive), und möglicherweise könnten einige dieser Abfragen Spezifikationen akzeptieren.

Das klingt nach viel Aufwand, aber anders als die benutzerdefinierten Abfragen ist der größte Teil des Codes, zumindest wenn Sie ein modernes ORM verwenden, sehr einfach zu implementieren, also verwende ich Vererbung (ReadWriteRepositoryBase wo T: IAggregateRoot) und / oder Komposition (Aufruf einer RepositoryHelper-Klasse). Die Basisklasse verfügt möglicherweise über Methoden, die in allen Fällen gelten, z. B. GetById.

Hoffe, das hilft.

    
Colin Jack 25.10.2008, 16:46
quelle
2

Ich arbeite in PHP, aber ich habe etwas Ähnliches für meine Datenzugriffsebene eingerichtet. Ich habe eine Schnittstelle implementiert, die ungefähr so ​​aussieht:

%Vor%

Und dann funktioniert jedes meiner Datenzugriffsobjekte so:

%Vor%

Ich baue derzeit jede der Datenzugriffsobjektklassen manuell auf. Wenn ich also eine Tabelle hinzufüge oder eine existierende Tabelle in der Datenbank modifiziere, muss ich natürlich den neuen Code von Hand schreiben. In meinem Fall ist dies immer noch ein großer Schritt von unserer Codebasis entfernt.

Sie können jedoch auch die SQL-Metadaten verwenden (unter der Annahme, dass Sie über ein ziemlich solides Datenbankdesign verfügen, das Fremdschlüsseleinschränkungen und Ähnliches ausnutzt), um diese Datenzugriffsobjekte zu generieren. In der Theorie könnten Sie dann die DataAccessObject-Klasse mit einem Elternteil verwenden, um die Eigenschaften und Methoden der Klasse zu erstellen und sogar automatisch Beziehungen zu den anderen Tabellen in der Datenbank aufzubauen. Dies würde mehr oder weniger die gleiche Sache erreichen, die Sie beschreiben, denn dann könnten Sie die DataAccessObject-Klasse erweitern, um benutzerdefinierte Methoden und Eigenschaften für Situationen bereitzustellen, in denen manuell konstruierter Code benötigt wird.

Als Nebenbemerkung für die .NET-Entwicklung haben Sie sich ein Framework angesehen, das die zugrunde liegende Struktur der Datenzugriffsschicht für Sie behandelt, z. B. Subsonic? Wenn nicht, würde ich empfehlen, einen solchen Rahmen zu betrachten: Ссылка .

Oder für die PHP-Entwicklung würde ein Framework wie Zend Framework ähnliche Funktionen bieten: Ссылка

    
Noah Goodrich 27.09.2008 21:41
quelle
2

George Ich weiß genau, wie du dich fühlst. Billys Architektur macht für mich Sinn, aber die Notwendigkeit, einen Container, Imapper und Mapper-Dateien zu erstellen, ist schmerzhaft. Dann, wenn Sie NHibernate die entsprechende .hbm-Datei und in der Regel ein paar Unit-Test-Skripte zu überprüfen, alles funktioniert.

Ich nehme an, dass, obwohl Sie NHibernate nicht verwenden, Sie immer noch eine generische Basisklasse verwenden, lo laden / speichern Sie Ihre Container, d. h.

%Vor%

Ich nehme an, dass Sie ohne NHibernate Reflexion oder einen anderen Mechanismus verwenden würden, um zu bestimmen, was SQL / SPROC aufrufen soll?

Entweder dachte ich, dass DAO nur die grundlegenden CRUD-Operationen ausführen muss, die in der Basisklasse definiert sind, dann sollte es nicht nötig sein, benutzerdefinierte Mapper und Interfaces zu schreiben. Der einzige Weg, wie ich dies erreichen kann, ist Reflection.Emit zu verwenden, um dynamisch Ihr DAO dynamisch zu erstellen.

    
Owen 29.09.2008 12:04
quelle
1

Ich verwende auch das Repository-Muster für mein DAO und ich bin ziemlich glücklich damit. Ja, Sie enden mit einigen kleinen Klassen, aber es ist sehr wartungsfreundlich. Es ist ein bisschen leistungsfähiger, wenn Sie die IQueriable Schnittstelle (LINQ.) Benutzen. Sie könnten auch Generics (etwas wie T GetById<T>(int id) ) benutzen, wenn Sie eine ziemlich konsistente Datenbankstruktur haben.

    
Mladen Mihajlovic 27.09.2008 21:40
quelle
0

Der Hauptgrund für den zweiten Ansatz liegt im Komponententesten - das Verspotten großer Factory-Methoden wie SimpleObjectRepository erfordert mehr Arbeit, als nur ICustomerDao auszuspionieren. Aber mein Projekt geht mit dem zweiten Ansatz für stark verwandte Objekte (wo die meisten in jedem Unit-Test verwendet werden), weil es die mentale Belastung aufhebt und sie wartbarer macht.

Ich würde herausfinden, was Ihr System wartungsfreundlicher und leichter verständlich macht und das macht.

    
ARKBAN 29.09.2008 12:12
quelle