Ist eine 3 Schichten Architektur mit eigener DAL immer empfehlenswert?

image

Wenn es darum geht eine “normale” Webapplikation zu bauen, dann kommt man meist auf eine 3 Schichten Architektur heraus. Der meistgelesenste Blogeintrag dreht sich um diese Architektur, allerdings kommen bei mir immer mal wieder Zweifel auf, ob man nicht zuviel Aufwand in der DAL betreibt – immerhin gibt es tolle OR Mapper. Doch auch damit bin ich bereits auf die Nase gefallen und hab mich von dem Gebrauch dieser Linq2Sql oder Entity Framework generierten Model-Klassen distanziert. Ayende Rahien hat aber mal wieder meine Zweifel geweckt…

3-Schichten Architektur

Ayende selbst ist bekennender Fan von NHibernate (immerhin hat er es mitentwickelt) und mag auch das Repository Pattern wohl auch nicht mehr so.

Allerdings sind die Grundgedanken vollkommen nachvollziehbar. Die Grafiken stammen aus einem von Ayendes Posts. Der normale Aufbau wäre bei einer 3-Schichten Architektur so:

image

Der Ablauf sobald ein User z.B. auf einen bestimmten Eintrag auf einer Website klickt wäre so:

image

Ihn stört es, dass man durch Repositories noch eine weitere Abstraktionsebene aufbaut. In den meisten meiner Projekte hatten wir es auch so umgesetzt und in dem “Data" Layer noch einen ORM, wie z.B. Linq2Sql oder das Entity Framework benutzt.

Zu viel Abstraktion?

Die Frage die ich mir stelle: Lohnt sich der Aufwand (und die Kosten für den Kunden) den Data-Layer zu kapseln, wenn man ohnehin ein OR Mapper benutzt?

Kann man denn realistisch und mit vertretbaren Aufwänden überhaupt eine gescheites IUserRepository bauen? Ayende hat ein paar Punkte aufgezählt, warum die Kapselung eines Data Access Layers unnötig auch nicht wirklich umsetzbar ist. In einem weiteren Post geht er genau ein und zeigt anhand des Membership Providers (den ich persönlich für grausam erachte), dass es vergebene Mühe ist.

Durch die Kapselung wird es leichter mit Unit-Tests die Businesslogik zu testen!

Durch ein Projekt wo wir in der Businesslogik direkt Linq2Sql und das Entity Framework eingesetzt haben, war es äußerst mühsam bis unmöglich Unit-Tests zu schreiben. Daher war es für die Testbarkeit der Businessschicht enorm wichtig, dass man die DAL auch mocken kann. Allerdings sind die beiden OR/M ja nicht die einzigen .NET OR/M…

NHibernate

Ayende ging auch in einem Post auf das Mocking von NHibernate ein und wie man auch Unit-Tests abbilden kann.

Die große Frage (und warum ich diesen Post schreibe)

Mach ich mir das Leben selber kompliziert indem ich versuche die DAL zu kapseln? Nehm ich vielleicht einfach nur die falschen OR/Mapper? Ist NHibernate so toll und kann mir jemand ein tolles Beispielprojekt samt Unit-Tests zeigen? :)

Ist eine 3-Schichten Architektur am Ende immer erstrebenswert? (Achtung: Ich geh hier von einem 0815 Webprojekt aus und zählen den OR/Mapper nicht als direkte Schicht, sondern ich würde direkt in der Business-Schicht NHibernate arbeiten. Dass es im Embedded Bereich andere Spielregel gibt ist mir klar ;) )


Kick It auf dotnet-kicks.de
Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf dem Laufenden bleiben willst, abonniere unseren RSS Feed oder folge uns auf Twitter.

About the author

Written by Robert Mühsig

Robert Mühsig (@robert0muehsig) ist Webentwickler und beschäftigt sich mit Web-Frameworks (vor allem dem ASP.NET MVC Framework) und scheut sich auch nicht vor Javascript. Ansonsten bloggt er über all jene Probleme, die ihm über den Weg laufen. Seit 2008 ist er Microsoft MVP für ASP.NET und er arbeitet bei der T-Systems Multimedia Solutions GmbH in Dresden. Treffen kann man ihn online via Twitter (@robert0muehsig) oder dieser Seite oder bei der .NET User Group Dresden.

26 Responses

  1. Ich arbeite auch exakt so, wie in seinem Worst-Case-Szenario oben abgebildet, d.h. mit IRepository.

    Dabei treten natürlich oft diese Fälle auf, in denen Daten vom Service aus dem Repository geholt und einfach nur durchgeschleift werden (nicht testens-wert btw. ;) ). Auf der einen Seite gut, auf der anderen Seite natürlich stupider, dummer Code.

    Auf der anderen Seite ist es doch so: die dahinterliegende Datenbank ändert sich in einem von 100 Projekten, wenn man mal von Software ausgeht, die dies als Feature beinhaltet (z.B. ne Foren-Software, die mit MSSQL oder MySQL arbeiten kann). Und selbst wenn sie sich änderte, würde man ja mit nHibernate oder Entity Framework keine Probleme bekommen.

    Und ob ich nun im Service auf den ORM zugreife oder aufs Repository, ist eigentlich relativ egal.

    Was für mich als Frage noch bleibt: wo mappt man auf seine Domain Objects/POCOs? Ich will definitiv nicht die vom ORM generierten Objekte (nehmen wir mal LinqToSql) applikationsweit verwenden.

    Reply
  2. Nachtrag: “von Software absieht” … wo ist der Edit-Button? ;)

    Reply
  3. Hallo Robert,

    sehr gute Frage :-D
    Meine Antwort: It Depends!

    Für 0815 Projekte rentiert sich das definitiv nicht. Hier ist der OR-Mapper (welcher auch imemr) definitiv dein DAL. Wieso einen eigenen spezialisierten Layer wenn die Arbeit vom OR-Mapper erledigt wird. In den meisten Fälle wird eh nur die Abfrage durch den BL und den DAL an den OR-Mapper durchgeschoben. Da gibt es weder viel zu testen noch irgend eine Logik.

    Erst wenn die Austauschbarkeit des OR-Mapper, des Datenbanksystems, etc. gefordert wird, macht ein DAL erst wirklich Sinn.

    Viele Grüße
    Jürgen

    Reply
  4. Hallo Robert,

    Ich kann deine Bedenken sehr gut verstehen. Die ganze Abstraktion ist schön und gut und hat mitunter das Ziel die entsprechende Schicht auszutauschen. Aber wie oft mussten wir eine Schicht austauschen, da der gekappselte OR Mapper ausgetauscht wurde?

    Ist es eher nicht der Sinn von einer DAL die auszutauschen damit man mehrere Datenbanken bedienen kann? Nimmt mir hier der OR Mapper nicht die Arbeit ab?

    Wenn ich bedenke das ich heute nochmal ein altes aktuelles Projekt angefasst habe und dort zwei DAL’s gibt, eine für MSSQL und eine für PostgreSQL, dann bin ich froh diese Abstraktion durchgeführt zu haben. Diese DALs sind nicht mit irgendeinem OR Mapper geschrieben sondern pures ADO.NET. Wenn nun noch eine Datenbank dazukommen sollte, könnte ich doch dann ein OR Mapper verwenden?

    Jegliche Abstraktion ist nur so gut wie Sie verwendet wird. Ohne eine Verwendung ist eine Abstraktion schlicht weg Leergeld.

    Reply
  5. @Thomas
    > wo mappt man auf seine Domain Objects/POCOs?
    Das erledigt sich mit NHibernate da du deine eigenen Objekte nutzen kannst: http://aspnetzone.de/blogs/juergengutsch/archive/2009/09/28/zweiter-start-mit-nhibernate.aspx

    Reply
  6. @Jürgen: nHibernate schaue ich mir erst mit Version 3 an, auf LINQ werd ich nicht verzichten ;)

    Reply
  7. @Thomas, du kannst auch mit NHibernate LINQ verwenden ;-)

    Reply
  8. @Jürgen: Link? Laut Albert wird es erst mit 3.0 nativ implementiert. Keine Lust jetzt wieder irgendwie drumrumzufrickeln.

    Grund: Repository + LINQ to SQL funktionieren einwandfrei. Wenn sich meine Datenbank ändert lösche ich alle Objekte im L2Sql-Designer und ziehe sie neu rein, speichere ab und passe nur noch das Mapping im Repository an. Nirgendwo, an keiner Stelle, irgendwelche komischen Strings und SQL-Abfragen.

    Das ist ein Level an Bequemlichkeit, das mir ne andere Lösung erstmal bieten soll. Und zusätzlich ist es schön abstrakt und lässt sich ohne jeden Umstand mocken :) .

    Reply
  9. Und wie testet man dann NHibernate Kram in der Business Schicht? Mit einer InMemory DB wie in Ayendes Post beschrieben?

    Reply
  10. @Jürgen: Irgendwie hab ich ein ungutes Gefühl wenn ich in meinem Model irgendwelchen OR/M Kram mit reinpacken muss, wie bei deinem Beispiel:
    public class Task: Entity
    { … }

    Reply
  11. @Thomas
    Link: siehe oben, ganz am Ende des Blogbeitrages ;-)

    Verwechselst du gerade LINQ mit LINQ to SQL? ;-) Wenn NHibernate Liste zurückkgibt, die irgendwie IEnumerabel implementieren kannst du definitiv LINQ nutzen. LINQ to NHibernate ist eine andere Geschichte.

    Klar ist LINQ to SQL bequemer, bis es einen entsprechenden Designer für NHibernate gibt, wird es noch etwas gehen. Aber mir ging es hier eigentlich um den “Code First”-Ansatz und die Möglichkeit so mit NHibernate die eigenen DTOs zu verwenden und sich das Mapping zu sparen, das mit LINQ to SQL nötig ist.

    Reply
  12. @Robert, das ist von mir um Gemeinsamkeiten der DTOs zu kapseln, nicht vom OR-Mapper

    Reply
  13. @Robert, Tests: ja, zum Beispiel oder du Mockst die NHibernate Objekte. It Depends: Je nach dem was du halt testen willst.

    Reply
  14. @Jürgen: Was meinst du mit “Gemeinsamkeiten der DTOs zu kapseln”? Hast du noch ein extra Domain Model?

    Reply
  15. @Robert, kannst du in dem Demoprojekt sehen, dass ich in meinem Blogbeitrag (link oben) angehängt habe. Hat nichts mit NHibernate zu tun:
    public class Entity: IEntity where T: IEntity
    {
    protected Entity()
    {
    Initialize();
    }

    protected virtual void Initialize()
    {

    }

    public virtual Guid Id { get; set; }

    public virtual bool Equals(T obj)
    {
    return Id == obj.Id;
    }
    public override bool Equals(object obj)
    {
    return Equals((T)obj);
    }
    public override int GetHashCode()
    {
    return base.GetHashCode();
    }
    }
    (Hm… Die Klasse muss eigentlich abstract sein)

    Reply
  16. @Jürgen: Ja, Missverständnis. LINQ stand als Synonym für Linq to Xyz. Ich will natürlich die Datenbankabfragen mit LINQ direkt machen und keine dreckigen Strings mehr benutzen ;-) .

    Und das ominöse Mapping (ORM=>eigene Objekte) kommt bei mir nur einmal in einer Map-Methode je Repository vor, damit habe ich kein Problem, zumal es typsicher ist und der Compiler gleich anspringt, anders als bei ner Config per XML o.ä.

    Reply
  17. @Thomas: klar :-) Du hattest nur die Frage gestellt, wo in Fall ohne Repository das Mapping statt finden soll.

    Reply
  18. Zum Thema Mapping auf POCOs: Das kann man auch automatisieren. Macht z.B. http://automapper.codeplex.com/

    Reply
  19. Betrachten wir das doch mal nüchtern.
    Vorteile eines DAL: Austauschbarkeit, Testbarkeit, Mapping auf POCOs.
    Nachteile: Verstecken der Features des O/R-Mappers bzw. aufwändiger Nachbau dieser, höherer Aufwand weniger Nutzen!?

    Mal ein kleiner Auszug aus meinem Projektleben. Wir haben vor einem Jahr auf einen DAL verzichtet und sind damit ordentlich an die Wand gefahren. O/R-Mapper war das Entity Framework inkl. einer Lazy-Loading “Erweiterung”. Wir haben die generierten Objekte auch in der gesamten Anwendung genutzt. Großes Problem dabei ist, dass die Datenstruktur der Datenbank die Datenstruktur der Anwendung bestimmt. Doof.
    Ein weiterer Nachteil davon: die ganze Anwendung ließ sich nicht Unit-Testen.

    Was müsste denn ein O/R-Mapper bieten, damit man ihn (vielleicht) ohne DAL nutzen könnte? In meinen Augen sind es 2 Punkte: Das Mapping auf POCOs und die Testbarkeit. Ausgetauscht wird (fast) nie…somit ist das vernachlässigbar.

    Was bei dem Projekt außerdem noch auffiel: Es wurde eine Vielzahl von Queries mehrfach geschrieben. Es war eben einfach, diese in der Businesslogik zu definieren. Jeder für sich..immer wieder. Ob der Grund dafür die Faulheit der Enwtickler war, diese Geschichten nicht in eigene Methoden auszulagern oder sowas dann eben einfach passiert will ich nicht beurteilen. Jedenfalls macht es die Wartbarkeit des Codes bei DB-Änderungen nicht einfacher. Schließlich muss ich an mehreren Punkten die Queries anpassen. Und das will ich nicht.

    Fazit: Ich bin mir unschlüssig. Bisher fand ich das Repository-Pattern super und wir haben es bei dem Redesign des Backends auch eingesetzt und fahren damit echt gut. Vielleicht ist doch die Projektgröße entscheidend!? Aber wer kann diese schon zu Beginn eines Projektes bewerten??

    Reply
  20. Also was ich so aus den Kommentaren raushöre: Ich hab wohl den falschen OR-Mapper genommen und sollte mal ein Blick auf NHibernate werfen.
    @Jürgen: Irgendwelche IRepositories hast du also nicht mehr, sondern machst in deiner Business Schicht die NHibernate Session auf und arbeitest damit?

    Reply
  21. @Robert, ich glaube nicht, dass du den falschen OR-Mapper genutzt hast. Wenn du OR-Mapper nutzt, mit denen du nicht die eigenen DTOs nutzen kannst, musst du halt im BL mappen (eher unschön, irgendwo hatte ich aber mal was zu Code First mit LINQ to SQL gelesen, da sollte es auch gehen)

    Doch, in der Regel nutzt ich das Repository-Pattern und gehe nur dort auf die OR-Mapper (egal welche). In dem Blogbeitrag habe ich nur ein einfaches (nicht repräsentatives!!) Projekt erstellt. In der Regel habe ich eine 4-Schicht Architektur (http://aspnetzone.de/blogs/juergengutsch/archive/2009/12/30/linq-und-linq-to-sql.aspx
    Die vierte Schicht ist Vertikal und enthält die Interfaces und DTOs)
    Und Ja: Würde ich in einer 0815-Anwendung einen OR-Mapper verwenden, würde ich den im BL direkt auf den OR-Mapper gehen.

    Reply
  22. @jürgen
    mit ienumerable hast du aber kein defered execution, da brauchts iqueryable.

    Im nomalfall braucht es keine repository schicht, wenn ein ormapper genutzt wird.
    Die würde ich nur einbauen, wenn abstraktion nötig ist (bspw zus. Zugriff auf xml),
    oder um die entwickler eimzuschränken.

    Das wäre dann iqueryable vs methoden mit argumenten, und nur die nutzen nhibernate direkt.

    Gruss peter

    Reply
  23. Ein eigener DAL hat ganz klar den Vorteil, dass man auch auf Unerwartete reagieren kann, ohne irgendwelche Mapper oder Frameworks über den Haufen werfen zu müssen, auch wenn das bedeutet etwas mehr zu coden.

    Wir haben diese Fleißarbeit “ApexSQL Code” überlassen. Ein Template, eine View oder Tabelle und hinten kommt der passende Code heraus. Datenbänkänderungen reduzieren sich da auf Copy-Paste. Mit einem selbst erdachten Pattern in Form von Templates sind wir so flexibel wie es nur geht, was man von Linq und Co. oft nicht behaupten kann.

    Den eigenen Code hat man zudem auch unter Kontrolle. In komplexen Projekten auf Blackboxes zu vertrauen, kann sich als fatal erweisen, weil die EINE Anforderung eben dann nicht geht und Workarounds entwickelt werden müssen.

    Wir leben ja fast alle von der Erstellung von Individuallösungen für unsere Kunden oder der eigenen Firma. Warum soll das nicht auch für die eingesetzten Werkzeuge gelten?

    Reply
  24. Ihr verwirrt mich gerade ein bischen:
    - manche schreiben dass ein DAL nie schlecht ist. Auch wenn er nichts anderes macht als die Abfragen dem ORM zu übergeben und das Result dann einfach zurück in die Service Schicht zurück zu geben (evtl. davor noch ein Mapping von ORM Objekten zu den normalen Business Objects)
    - andere schreiben dass man auf den DAL komplett verzichten kann und den ORM direkt aus der Business Schicht ansprechen kann

    Ja wie denn nun? ;) Und wie geht ihr mit XML, Textdateien, Images usw. um die z.B. auf dem Filesystem liegen? Auch in der Business Schicht?

    Reply
  25. Hier gibt es einfach nicht “die” Wahrheit. Mir scheint dass alle die mit nem mächtigen ORM arbeiten, und hier sehe ich bisher nur nHibernate, Ayendes Ausführungen gegenüber ein wenig offener sind.

    Ich z.B. bin bisher mit dem Repository-Pattern, und der Art und Weise wie du sie nochmal zusammenfasst, noch an keine Grenze gestoßen und sehe (derzeit) dass die Vorteile für mich überwiegen:

    Es ist testbar, wenn ich will; das Mapping ist kein großer Aufwand; und überhaupt beruhigt mich diese Abstraktion noch ein wenig mehr in Hinblick auf die unbekannte Zukunft.

    Ich hatte neulich ein schönes Beispiel, als ich eine etwas ältere Komponente erweitert habe. Als ich die entwickelt habe, habe ich bereits auf Repositories gesetzt (sie nur nicht so genannt), aber noch mit ADO.NET und Stored Procedures gearbeitet. Dadurch, dass es abstrahiert war, war es überhaupt kein Problem für die neuen Features zum Datenbankzugriff nun Linq to Sql zu verwenden – davon hat die übrige Anwendung nichts mitbekommen.

    Aber das ist meine eigene Sicht der Dinge – es bleibt wie schon gesagt bei “it depends”, du musst es wohl selbst ausprobieren und deine eigene Entscheidung treffen ;-) .

    Reply

Comment on this post

Letzte Posts

  • Carriage Return / Neue Zeile in Textareas

    Eine kleine Aufgabe: Jede neue Textzeile (Carriage Return/Wenn man Enter drückt ) in einer Textarea soll ein Element in einer Auflistung sein – wie mach ich das jetzt am einfachsten? Eigentlich ein grundlegendes Element im Web und der Nutzer macht bewusst Absätze – daher wäre es nur gerecht, wenn man das auch entsprechend würdigt. Kleine ...

  • image.png
    Doom, Quake, Wolfenstein & co. Source Code auf GitHub

    id Software, die Macher von Doom, Quake, Wolfenstein & co., stellen regelmäßig ihre älteren Spieltitle als Open Source zur Verfügung. Das Ganze runterzuladen fand ich bisher immer recht mühselig, allerdings gibt es seit kurzer Zeit die Sourcen auch auf GitHub. Darunter Spiele wie Doom 3, Quake 3, Wolfenstein für iOS. Wer also schon immer mal ...

  • image.png
    Twitter Bootstrap 2.0 released & “Release Präsentation”

    Wie bereits vom Twitter Bootstrap Team angekündigt wurde offiziel die Version 2.0 des UI Toolskits “Twitter Bootstrap” veröffentlich. Zudem wurden die Slides, welche bei der Release Party gezeigt wurden auch veröffentlicht: Downloads finden sich auf der Twitter Bootstrap Seite auf GitHub. Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf ...

  • image.png
    Javascript zu Dart Translator

    Dart, Google Javascript Alternative, wurde vor ein paar Monaten vorgestellt und die Webentwickler Szene ist noch etwas gespalten, ob Dart nun überflüssig ist oder einfach nur cool und längst überfällig ist. Um die Sprache näher zu erläutern hat Google die grundlegenden Javascript Basics nach Dart übersetzt. Das Ergebnis ist der “Translator”. Der Name mag momentan ...

  • Twitter Bootstrap 2.0–“Beta”

    Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0. Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt. Im Vergleich zur aktuellen ...

Support us!

Facebook