MB: Repository Pattern erklären + Anwendungsbeispiel

moin Community,

kann mir bitte jemand Dieses Pattern erklären das das es ein Idiot versteht und ein Anwendungsbeispiel, am besten natürlich in PHP, bereitstellen? Anwendungsbeispiele helfen immer sehr. Ich weis das dieses Pattern eine Abstration zur Datenbank darstellt. Das Programm kann dann über dieses Pattern Objekte einholen, verändern oder senden. Das Pendant zu O/R-Mapper, so mein Wissensstand. Bitte körrigiert es wenn ich mich irre oder was falsch verstanen habe.

Auf youTube finde ich einiges aber die Tutoren nuscheln auf englisch ohne pause. Das bereitet mir kopfschmerzen und der Untertitel in dieser Combie nutzt mir ziehmlich wenig 😕.

vlg MB

PS.: Was ist DATA ACCESS LAYER? Sowas wie Repository?

  1. Tach!

    Ich weis das dieses Pattern eine Abstration zur Datenbank darstellt.

    Nicht nur. Man kann auch Daten aus anderen Quellen darüber bereitstellen.

    Das Programm kann dann über dieses Pattern Objekte einholen, verändern oder senden. Das Pendant zu O/R-Mapper, so mein Wissensstand.

    Ein ORM ist eine Schicht, die zwischen dem relationalen DBMS und einem objektorientierten System vermittelt. Ein Repository ist nicht etwa ein Gegenstück dazu, sondern eine Ergänzung. Ein ORM hat die Aufgabe nur die grundlegende Übersetzung vorzunehmen. Ein Repository spricht sozusagen nach unten hin mit dem ORM oder mit anderen Datenquellen, nach oben hin stellt es das bereit, was die Anwendung fachlich braucht. Die Anwendung muss nicht wissen, dass man für die Daten, die sich braucht, beispielsweise Joins oder eine Gruppierung auf die Datentabellen vornehmen muss. Sie will nur einfach ins Repository greifen können und die Daten mundgerecht bekommen. Oder in anderer Richtung für das Speichern bearbeiteter Daten übergibst du dein Objekt. Vielleicht ist es sogar verschachtelt, wenn es eine größere Struktur aus mehreren Einzelobjekten ist. Das Repository nimmt es gegebenenfalls auseinander und veranlasst die Speicherung der Einzelteile in Richtung des verwendeten Datenspeichers.

    Für jede der fachlichen Anforderungen an die Datenhaltung muss das Repository eine Funktion bereitstellen. Einen ORM wird man nur einmalig im System haben, es sei denn, man muss mehrere unterschiedlichen Datenbanken bedienen, die alle einen eigenen ORM benötigen. Repositorys hingegen braucht man soviele, wie Themenbereiche abzudecken sind. Man möchte ja die Übersicht behalten und erstellt sich deshalb kein Monster-Repository sondern getrennte für die einzelnen Themen.

    Wenn du ein Framework schreibst, wirst du vielleicht ein ORM zur Verfügung stellen, aber eigentlich sollte sich das der Verwender selbst aussuchen können. Für Repositorys brauchst du aber normalerweise nichts vorzusehen, die muss sich der Verwender anhand seiner Aufgabenstellung selbst schreiben.

    PS.: Was ist DATA ACCESS LAYER? Sowas wie Repository?

    Wenn du ganz grobe Schichten zeichnen möchtest, wie deine Anwendung aussiehst, dann bezeichnet der DAL ganz allgemein die Schicht, in der die Datenzugriffslogik drinsteckt. Was das dann konkret ist, ist nicht definiert. Das kann ein ORM sein, oder eine beliebige andere DBMS-Zugriffstechnik, oder etwas das auf Dateien zugreift, oder irgendwelche über Netzwerk erreichbare Datenquellen verwaltet. Auch alles das zusammen, falls man das für seine Anwendung braucht.

    dedlfix.

    1. moin dedlfix,

      soweit habe ich das Begriffe :-). Danke dir. kannst noch simple Code beipiele zu Klassen geben und was sich dahinter verbirgt? Ich will das theroretisch Begriffene am praktischen verstehen.

      vlg MB

      1. Tach!

        soweit habe ich das Begriffe :-). Danke dir. kannst noch simple Code beipiele zu Klassen geben und was sich dahinter verbirgt? Ich will das theroretisch Begriffene am praktischen verstehen.

        Ich verweise da mal einfacherweise auf ein Tutorial von ASP.NET MVC, bevor ich mir selbst ein Anwendungsfall ausdenke.

        In den ersten beiden Code-Beispielen auf der Seite sieht man ein Repository zum Verwalten von Studenten/Schülern. Das ist recht einfach gehalten, es beinhaltet die CRUD-Grundfunktionalität (Create, Read, Update, Delete). Für das Lesen gibt es zwei Methoden, eine für alle und eine für einen einzelnen Datensatz.

        Im weiteren Verlauf sieht man, wie der Controller mit dem Repository umgeht. Da ist noch ein gravierendes Problem mit der Index-Action drin. Sie macht mit dem Filtern und Sortieren noch zu viel Datenzeugs. Das sollte ins Repository verlagert werden, und das geschieht auch noch weiter unten. Ansonsten geht der Artikel natürlich sehr auf die Eigenheiten bei ASP.NET MVC und dem Entity Framework ein. Da sind Details, die du für das allgemeine Verständnis ignorieren kannst. Die ersten beiden Beispiele sind eigentlich schon ausreichend für dein Anliegen. Gezeigt wird dann auch noch das UnitOfWork-Pattern, das man verwendet, wenn das gleichzeitige Arbeiten mit mehreren Repositorys nötig wird.

        dedlfix.

        1. moin dedlfix, Danke!

        2. moin dedlfix,

          ich möchte auf die Frage die ich bereits von 22.08 gestellt habe aufgreifen. Bezogen auf das Beispiel Repository (leider in Englisch aber nicht tragisch) erkenne ich die strukturen aber nicht die eigentliche Funktion, weil es, wie mir scheid, BuiltIn-Funktionen sind (oder externe Funktionen, Packages, PlugIns). Sorry ich bin überfordert.

          Ich hatte gedacht das die BuisenessLogic und Database das Repository eine Zwischenklasse/n ist/sind die Objekte so modifiziert, dass sie sich bequem in eine Ablage stapeln lassen, in dem Fall Database. Im beispiel sehe ich nichts von SQL noch keine Interaktion der Datenbank mit SQL.

          vlg MB

          1. Tach!

            ich möchte auf die Frage die ich bereits von 22.08 gestellt habe aufgreifen. Bezogen auf das Beispiel Repository (leider in Englisch aber nicht tragisch) erkenne ich die strukturen aber nicht die eigentliche Funktion, weil es, wie mir scheid, BuiltIn-Funktionen sind (oder externe Funktionen, Packages, PlugIns).

            Die eigentlichen Funktionen sind hier nur Ein- bis Zweizeiler, weil das Entity Framework (ein ORM) die meiste Arbeit erledigt, um mit dem DBMS zu sprechen. Zudem ist für dieses Beispiel nicht viel zu tun. Stell dir vor, es käme noch weitere Funktionalität hinzu, wie das Filtern nach bestimmten Werten oder das Sortieren. Der Controller gibt nur die Parameter ans Repository und das kümmert sich darum, mit dem ORM (oder welcher Datenbankzugriffslogik auch immer) zu sprechen und die Parameter wie gewünscht zu übergeben.

            Kurz gesagt: Der Controller möchte nur Daten haben oder loswerden. Es interessiert ihn nicht, wie genau das passieren soll, das übernimmt das Repository.

            Ich hatte gedacht das die BuisenessLogic und Database das Repository eine Zwischenklasse/n ist/sind die Objekte so modifiziert, dass sie sich bequem in eine Ablage stapeln lassen, in dem Fall Database. Im beispiel sehe ich nichts von SQL noch keine Interaktion der Datenbank mit SQL.

            Soweit richtig, aber da ist noch eine weitere Schicht zwischen Repository und der Datenbank, nämlich der ORM in Form des Entity Frameworks im Falle des verlinkten Beispiels.

            Controller - Repository - Datenbankzugriff (z.B. ORM) - Datenbank

            Es kann sein, dass die Business-Objekte umgebaut werden müssen. Ein Schüler-Objekt, das eine Liste der belegten Kurse in einer Eigenschaft hat, wird im DBMS in zwei Tabellen abgelegt. Das Repository kümmert sich darum, dass in Richtung ORM die beiden Tabellen angesprochen werden. Es kann auch sein, dass das Repository weniger zu tun hat, nämlich dann, wenn der ORM bereits Informationen zu den Beziehungen hat (zum Beispiel aus den Fremdschlüsseln analysiert), und selbständig die Kurse des Schülers liefern kann. Dann muss das Repository nur noch in Auftrag geben, die Daten von Schüler X zu besorgen, "aber bitte inklusive Kurse", anstatt neben den Schülerdaten eine zweite Abfrage zu den Kursen mit where ID_Schüler=X abzusetzen. Wenn sich die Daten auf solch eine einfache Weise aus dem ORM holen lassen, kann man auch auf die Zwischenschicht Repository verzichten. Und das ist auch der Fall, wenn man die Einsteigertutorials zum ASP.NET MVC anschaut, die kommen ohne Repository aus.

            Nochmal der Hinweis, Respositorys sollten/müssen kein Bestandteil eines MVC-Frameworks sein. Die Businesslogik und damit welche Abfragen an die Datenhaltung gestellt werden müssen, kennt nur der Verwender. Es gibt nichts, das man ihm in irgendeiner Form sinnvoll vorbereiten könnte, das über grundlegendes CRUD hinausgeht. Aber das bietet bereits ein guter ORM.

            dedlfix.

            1. moin dedlfix,

              ah ok, danke. Jetzt leuchtet mir einiges ein. Denn zu Repository-Pattern habe Ausschließlich Code-Beispiele in C# mit EntityFramework gesehen und Nur in dieser Konstellation 😕.

              Ist denn sowas in dieser Konstellation möglich?

              class UserRepository {
              
                private $db;
                
                public function __construct( /* params */) {
                  $this->db = new Database( /* params */ );
                }
                
                public add( User $user ) : void {
                  $this->db->sendData(
                    'INSERT INTO TABLE `tbl_users`( name, password ) VALUES ( ?, ? )',
                    [ 'name' => $user->name, 'password' => $user->password ]
                  ); 
                }
                
                public remove( int $id ) : void {
                  $this->db->sendData(
                    'DELETE FROM `tbl_users WHERE id = ?',
                    [ $id ]
                  ); 
                }
                
                public get( int $id ) : User {
                  return $this->db->getData(
                    'SELECT * FROM `tbl_users` WHERE id = ?;',
                    [ $id ]
                  ); 
                }
                
                public getAll( int $id ) : User {
                  return $this->db->getAll(
                    'SELECT * FROM `tbl_users`;',
                    []
                  ); 
                }
              }
              

              fällt das auch unter Repositories?

              vlg MB

              1. Tach!

                Ist denn sowas in dieser Konstellation möglich? [...] fällt das auch unter Repositories?

                Im Prinzip ja. Aber du wirst nicht wollen, dass du für jedes Businessobjekt SQL-Querys schreibst, zumindest nicht für die CRUD-Funktionalität. Sowas lagert man besser in einen ORM aus.

                dedlfix.

                1. Ist denn sowas in dieser Konstellation möglich? [...] fällt das auch unter Repositories?

                  Im Prinzip ja. Aber du wirst nicht wollen, dass du für jedes Businessobjekt SQL-Querys schreibst, zumindest nicht für die CRUD-Funktionalität. Sowas lagert man besser in einen ORM aus.

                  Ok. Ich könnte ja Zuständigkeit bereiche unterteilen damit man sich nich mehr um einiges kümmern muss z.B.

                  abstract class Repository {
                    private $context;
                    public function __construct( string $context ) {
                      $this->context = $context:
                    }
                  }
                  
                  class UserRepository extends Repository {
                    public function add( User user ) : void { /* ... */ }
                    public function delete( int $id ) : void { /* ... */ }
                    public function get( int $id ) : User { /* ... */ }
                    public function getAll() : array { /* ... */ }
                  }
                  

                  außerdem ist das ja zu test zwecken. Das ORM zu nutzen wird mein nächstes Ziel

                  vlg MB

                  1. Hallo MB,

                    das Repository sollte noch etwas mehr tun, als nur die SQLs zu kapseln. Wenn es sich auf ein ORM wie EntityFramework stützt, bekommt es das sozusagen gratis, aber zur Grundfunktionalität des Repositories gehört das Management von Objektinstanzen der Domäne, für die das Repository gedacht ist. Das heißt, du brauchst noch Folgendes:

                    • Anlegen neuer Objekte (bzw. übernehmen neuer Objekte ins Repository)
                    • Zurückschreiben geänderter Objekte in die Datenbank (abstrakter: auf das Speichermedium)

                    Das Repository muss zu diesem Zweck ein Verzeichnis der von ihm gelesenen Objekte führen, und auch die neu angelegten Objekte kennen. Es braucht MINDESTENS eine Methode wie

                    public function save( User $user ) : void { /* ... */ }
                    

                    diese Methode kann dann unterscheiden, ob da ein zuvor gelesenes Objekt ankommt (dann macht sie einen Update) oder ob es ein neues Objekt ist (dann macht sie einen INSERT und besorgt eine ID). Die von Dir erwähnte add() Methode sollte eher dazu dienen, ein neu angelegtes Objekt unter Repository-Kontrolle zu bringen (damit Referenzen funktionieren), ohne es deswegen gleich in die DB zu schreiben.

                    Ein get($id) greift dann zunächst einmal auf den Cache zu, nicht auf die DB, und ein delete($user) markiert den User als "gelöscht". Ob man den DELETE sofort auf die DB jagt, kann man sich dann überlegen.

                    Auf diese Weise ist das Repository gleichzeitig eine fachliche Zugriffsschicht zur Datenablage und ein Cache für gelesene Objekte. In einem Request-Handler (sprich: ein PHP Script, dass Webseiten ausliefert) ist das durchaus legitim, da bleiben die Daten nicht lange im Speicher. Ein Langläufer müsste hier achtsamer sein und ggf. regelmäßig die Caches aktualisieren. Ein Requesthandler kann alles lesen, was er braucht, und nach Bedarf modifizieren. Wenn es die DB hergibt, gern in einer Transaktion. Zum Abschluss gibt's dann sowas wie ein "Save All" - was aber durchaus komplex sein kann, vor allem, wenn die DB Konsistenzprüfungen durchführt (Stichwort referenzielle Integrität). Ob das EntityFramework oder ein fertiges PHP ORM das korrekt und automatisch tut, weiß ich nicht, ich stelle mir den Algorithmus dafür jedenfalls schwierig vor. Deswegen baue ich meine Datenbanken gerne ohne automatische Konsistenzprüfungen und führe sie selbst im Code durch, das kann die Komplexität von Updates ungemein vereinfachen.

                    Rolf

                    --
                    Dosen sind silbern
                    1. moin RolfB,

                      [...] aber zur Grundfunktionalität des Repositories gehört das Management von Objektinstanzen der Domäne, für die das Repository gedacht ist. Das heißt, du brauchst noch Folgendes:

                      • Anlegen neuer Objekte (bzw. übernehmen neuer Objekte ins Repository)
                      • Zurückschreiben geänderter Objekte in die Datenbank (abstrakter: auf das Speichermedium)

                      Hab ich das nicht im Beispiel drinnen???

                      Die von Dir erwähnte add() Methode sollte eher dazu dienen, ein neu angelegtes Objekt unter Repository-Kontrolle zu bringen (damit Referenzen funktionieren), ohne es deswegen gleich in die DB zu schreiben.

                      Du meinst das zu serialisieren mit seialize() im Repository? Kannst du mir ein Objekt-Methoden-Beispiel geben wo das vorkommt

                      Ein get($id) greift dann zunächst einmal auf den Cache zu, nicht auf die DB, und ein delete($user) markiert den User als "gelöscht". Ob man den DELETE sofort auf die DB jagt, kann man sich dann überlegen.

                      Codebeispiel in PHP??? in C# habe ich "zuviele" Beispiele gesehen mit besagtem EntityFramework. Ob es gut is oder nicht sei dahingestellt, aber zum Verstendnis ein ORM zu verwenden, is nicht grade förderlich, sndern eher hinderlich.

                      Ein Requesthandler kann alles lesen, was er braucht, und nach Bedarf modifizieren.

                      Was ist ein Requesthandler? z.B. Router?

                      vlg MB

                      1. Das von @dedlfix angeführte Beispiel taugt auch aus meiner Sicht nichts, es verwirrt eher. Daher mal ein bischen Code von mir.

                        Aufgabe: Benötigt wird eine transparente Schnittstelle zur Datenhaltung einer Benutzerverwaltung.

                        Anforderung als Codebeispiel:

                        $this->DAL = array(
                          'users' = array(
                             /* hier sind alle Benutzer persistent
                                nach numerischer Identity */
                             '0' => array(
                               'name' => 'Vollstecker',
                               'phone' => '+30 123..',
                               'group' => 'admin'
                             ),
                             '1' => array(),
                             '2' => array()
                          ),
                          groups => array(
                             'public' = array(
                               'descr' => 'Jeder darf hier alles tun',
                               'title' => 'Public Realm'
                             ),
                             'admin' = array(
                               'descr' => 'Administratoren und Stellvertreter',
                               'title' => 'Administratoren'
                             )
                          )
                        );
                        

                        Die Anforderung zeigt gleichzeitig die Anwendung: Benutzer werden einfach in das Array eingetragen und solange im Speicher gehalten, bis $this->DAL->write(); aufgerufen wird. Erst write() schreibt die Daten ins Repository. Und wie diese beschaffen ist (Dateisystem oder Datenbank oder ..) weiß allein der Data Access Layer DAL.

                        DAL ist also gleichzeitig ein abstrakter Datentyp zu dem sich Methoden definieren lassen für den lesenden und schreibenden Zugriff auf bestimmte Benutzer, Gruppen und Eigenschaften.

                        Und: Allein die Anwendung bestimmt die innere Struktur und den Aufbau des abstrakten Datentypes der persistent gemacht wird.

                        So, Nun habe ich mal aus meiner Erfahrung heraus ein bischen Code gepostet. Vielleicht ist das ja verständlicher als das was andere machen, nämlich nur aus Wikipedia abtippen (und dafür auch noch ++ kriegen).

                        Finesse: Der DAL ist so beschaffen daß er nicht die gesamte Repository in den Hauptspeicher holt sondern nur den Zweig der gerade angefordert wurde, z.B. ein bestimmter Benutzer. Und ja, das geht auch mit PHP zu machen.

                        MfG

                        1. Tach!

                          Die Anforderung zeigt gleichzeitig die Anwendung: Benutzer werden einfach in das Array eingetragen und solange im Speicher gehalten, bis $this->DAL->write(); aufgerufen wird. Erst write() schreibt die Daten ins Repository. Und wie diese beschaffen ist (Dateisystem oder Datenbank oder ..) weiß allein der Data Access Layer DAL.

                          Da haben wir das Verständnisproblem. Ein Repository ist keine Datenbank, sondern eine Zwischenschicht zwischen der Datenhaltung und der Anwendungslogik, um den Abstand zu den spezifischen Anforderungen einer Anwendung und dem universell und allgemein gehaltenen Datenspeicher zu überbrücken.

                          Das was du im ersten Satz beschreibst ist das, was man üblicherweise unter einem Repository versteht. Der zweite Satz sollte mit dem Wort Datenbank (oder Dateisystem) enden.

                          dedlfix.

                          1. Mal unter uns: Das ganze Patterngedöns wird total überbewertet. Daß man Datenhaltung und Transport über einen speziellen Layer von der Anwendung trennt war für mich schon immer ausschließlich eine Frage der Zweckmäßigkeit. Insbesondere die Austauschbarkeit und Transparenz dieser Layers.

                            Beispiel:

                            $VAR1 = bless( {
                                             'Heizung_Arbeit' => {
                                                                   'default' => '0.3',
                                                                   'devid' => '0x1A1A-1',
                                                                   'max' => '1.0',
                                                                   'min' => '0.0',
                                                                   'period' => '10',
                                                                   'slider' => '1',
                                                                   'step' => '0.1',
                                                                   'times' => [],
                                                                   'type' => 'PWM'
                                                                 },
                                             'Heizung_Stube' => {
                                                                  'default' => '19.0',
                                                                  'max' => '30.0',
                                                                  'min' => '5.0',
                                                                  'slider' => '1',
                                                                  'step' => '1.0',
                                                                  'times' => [
                                                                               '7:00',
                                                                               '11:00',
                                                                               '22.0',
                                                                               '17:00',
                                                                               '22:00',
                                                                               '24.0'
                                                                             ],
                                                                  'type' => 'Temperature'
                                                                },
                                             'Hoflicht' => {
                                                             'default' => 'Off',
                                                             'flipflop' => '1',
                                                             'times' => [],
                                                             'type' => 'OnOff'
                                                           }
                                           }, 'Config::Tiny' );
                            

                            Der Dump zeigt die ursprüngliche Klassenbindung des Layers an Config::Tiny die jedoch in der Anwendung gar nicht sichtbar ist, weil die Daten initial über eine Methode der Factory $this->configini('Dateiname'); geholt werden.

                            Der Übergang des abstrakten Datentypes in einen anderen Layer, nämlich $this->{SESSION} ist 100%ig transparent. Und schließlich, nach einer Prüfung der Datenkonsistenz, gehts zurück auf die Festplatte über einen hard codierten Serializer der auch die Webservice Schnittstelle bedient.

                            Sämtliche Layer sind, von der Initialisierung über die Anwendung, Zwischenspeicherung in der Session bis zum HTTP Client durchsichtig (transparent), d.h., die Datenstruktur für den wahlfreien Zugriff ist überall dieselbe.

                            MfG

                            1. Tach!

                              Mal unter uns: Das ganze Patterngedöns wird total überbewertet.

                              Finde ich nicht. Sie sind eine gute Orientierungshilfe für diejenigen, die das Rad nicht ständig selbst neu erfinden wollen.

                              Daß man Datenhaltung und Transport über einen speziellen Layer von der Anwendung trennt war für mich schon immer ausschließlich eine Frage der Zweckmäßigkeit. Insbesondere die Austauschbarkeit und Transparenz dieser Layers.

                              Na, was glaubst du, haben die Programmierer im Sinn gehabt, die sich solche Konstrukte schrieben, sie immer wieder verwendeten, weil sie sich bewährt haben, und letztlich dem Kind einen Namen gaben und es als eine mögliche Musterlösung anderen Programmierern vorstellten?

                              dedlfix.

                              1. Na, was glaubst du, haben die Programmierer im Sinn gehabt, die sich solche Konstrukte schrieben, sie immer wieder verwendeten, weil sie sich bewährt haben, und letztlich dem Kind einen Namen gaben und es als eine mögliche Musterlösung anderen Programmierern vorstellten?

                                Wer sagt denn daß es Programmierer sind die dies tun? Also ich kenne einige Programmierer die seit Jahren mit Dependency Injection arbeiten ohne zu wissen daß das so heißt. Solche Beispiele lassen sich beliebig fortsetzen. Und als ich die Grundlagen zu meinem MVC Framework legte, gab es gar den Begriff MVC noch gar nicht.

                                Und bevor wir uns hier weiter rumstreiten: Es ist einfach so in der Praxis, daß verschiedene Entwickler an verschiedenen Orten völlig unabhängig voneinander auf gleiche oder ähnliche Lösungen kommen, also "das Rad ständig neu erfinden" um diesen dämlichen Spruch mal wieder zu strapazieren.

                                In der Entwicklung gibt es keinen Stillstand. Da sind Pioniergeist und Kreativität gefragt. MfG

                                1. Tach!

                                  Na, was glaubst du, haben die Programmierer im Sinn gehabt, die sich solche Konstrukte schrieben, sie immer wieder verwendeten, weil sie sich bewährt haben, und letztlich dem Kind einen Namen gaben und es als eine mögliche Musterlösung anderen Programmierern vorstellten?

                                  Wer sagt denn daß es Programmierer sind die dies tun?

                                  Ich verstehe den Sinn deiner Frage nicht. Wer sonst sollte das denn tun?

                                  Also ich kenne einige Programmierer die seit Jahren mit Dependency Injection arbeiten ohne zu wissen daß das so heißt. Solche Beispiele lassen sich beliebig fortsetzen. Und als ich die Grundlagen zu meinem MVC Framework legte, gab es gar den Begriff MVC noch gar nicht.

                                  Ja klar, warum auch nicht? Solche Muster entstehen ja nicht am Reißbrett, nach der Devise "heute erfinde ich mal ein Pattern", und werden dann Gesetz.

                                  Und bevor wir uns hier weiter rumstreiten: Es ist einfach so in der Praxis, daß verschiedene Entwickler an verschiedenen Orten völlig unabhängig voneinander auf gleiche oder ähnliche Lösungen kommen, also "das Rad ständig neu erfinden" um diesen dämlichen Spruch mal wieder zu strapazieren.

                                  Auch dem steht nicht entgegen, dass einer dieser Programmierer oder eine Gruppe davon sich mal hinsetzen und davon berichten, was für Lösungen sie immer wieder einsetzen. Dass solche Musterlösungen Namen bekommen haben, die von vielen anderen aufgeriffen wurden, ist nicht nur, dass die Masse gern einem Trend folgen wollen würde, sondern zu großen Teilen auch etwas mit Zweckmäßigkeit beim Kommunizieren zu tun. Spezialbegriffe vereinfachen diese enorm, weil aus einem Wort hervorgeht, was der andere meint, nachdem man die Bedeutung erfahren hat.

                                  In der Entwicklung gibt es keinen Stillstand. Da sind Pioniergeist und Kreativität gefragt.

                                  Und Kreativität ist, wenn man die Lösungen der anderen ignoriert, die bereits seit Jahren beschrieben und benannt sind? Findest du es kreativ, wenn man das uralte Wissen um die Erstellung von Brot ignoriert, selbst eine Herstellungsmethode entwickelt und das dann Butter nennt?

                                  dedlfix.

                                  1. Wer sagt denn daß es Programmierer sind die dies tun?

                                    Ich verstehe den Sinn deiner Frage nicht. Wer sonst sollte das denn tun?

                                    Unternehmer und Geschäftsleute. Weil es die Einzigen sind die daran Interesse haben (pekunär). Vielfach nämlich ist da draußen weder Pioniergeist noch Kreativität gefragt sondern Schablonenarbeit.

                                    Und Kreativität ist, wenn man die Lösungen der anderen ignoriert, die bereits seit Jahren beschrieben und benannt sind?

                                    Nein. Kreativität ist schon gegeben wenn man Lösungen Anderer aufgreift und weiterentwickelt. Ich schrieb ja nicht von "Ignorieren" sondern von "Überbewerten". D.h., wenn man Lösungen anderer recherchiert und aufgreifen will, muss man sie bewerten. Und genau da ist der Unterschied!

                                    MfG

                      2. Hallo MB,

                        nein, in deinem beispielhaften Interface hatte ich das nicht erkannt. Auf jeden Fall vermisse ich den UPDATE auf die DB.

                        Ein "Requesthandler" ist ein Typ von Programm, der Requests bekommt und Antworten liefert. Eine mit PHP gebaute Webseite ist genau das. Andere Programmtypen sind Desktop-Applikationen (dein Browser) oder Dienste (der Webserver).

                        Den Ablauf mit Code zu beschreiben wird aufwändig. Jedenfalls würde ich das Schreiben nicht auf serialize() verengen wollen, zumindest nicht, wenn die Datenhaltung in einer DB erfolgt. Ein serialize() neigt dazu, abhängige Objekte mit zu serialisieren, also einen ganzen Objektbaum flachzuklopfen, und wenn Du in einer DB speicherst findet das eher nicht statt.

                        Ein Repository, das ich mal gebaut habe, hält zu jedem Objekt, das es liefert, unter der Haube das Array fest, das aus dem DB-Lesezugriff herausgekommen ist. Die save() Methode vergleicht dann das Objekt mit diesem Array und generiert einen UPDATE-Befehl, der nur die geänderten Spalten zurückschreibt. Hat sich nichts geändert, wird auch nicht geschrieben. D.h. mein fachlicher Code kann am Ende einfach save() aufrufen und muss nicht drüber nachdenken, welche Objekte geändert wurden.

                        Der Grund für diese Vergleicherei ist, dass meine Fachobjekte keine get/set Properties haben, sondern einfache PHP Instanzvariablen verwenden. In C# oder Java würde ich ggf. setter verwenden, um im Objekt festzuhalten, welche Properties verändert wurden. Das Entity Framework kann, wenn man virtuelle get/set Properties verwendet, die Fachobjekte mit Runtime-Klassen überschreiben und führt diese Überwachung unter der Haube durch. Wenn man das in PHP selbst bauen will, läuft sowas auf viel Standardcode hinaus, den man ständig gleich schreiben muss. Ein Value-Vergleich im Repository ist dann einfacher.

                        Idee wäre also, dass Du für jede fachliche Domäne (Domänen sind etwas mehr als DB-Tabellen; eine Domäne kann sich über mehrere Tabellen erstrecken. Beispiel wäre "Fahrzeug" mit abhängigen Tabellen die sich auf Halter-Referenzen oder Verweise auf Versicherungsverträge beziehen) ein Repository hast. Dem sagst Du get($id) um ein "Root-Objekt" der Domäne abzurufen. Das Repository hat intern ein Array, in dem es unter $id ablegt, was es für diese id gelesen hat. Rufst Du $fahrzeugRepository->save($fahrzeug) auf (oder $fahrzeug->save() wenn das Fahrzeug sein Repository kennt), vergleicht das Repository die gespeicherten Werte mit dem Objekt und generiert die passenden Updates oder Inserts. Wenn es abhängige Objekte gibt, kann aus einem save() eine Menge Aktivität folgen, ggf. sogar DELETES wenn du eine abhängige Collection hast und aus dieser Collection Werte entfernt hast. Ein ORM Framework macht das für dich automatisch, ohne ORM programmierst Du es selbst.

                        Du kannst Dir im Repository einen schlankeren Fuß machen, wenn Du Domäne mit Table gleichsetzt. Dann brauchst Du dort keine Analyse der Abhängigkeiten, aber die fehlende Funktionalität (sprich: Pflege von Collections und DB-Beziehungen) implementierst Du dann im fachlichen Teil deines Programms. In meinem Repository habe ich das so gemacht. Vielleicht erweitere ich es nochmal - aber im Moment habe ich für das ganze Projekt keine Zeit.

                        Rolf

                        --
                        Dosen sind silbern
                        1. moin RolfB,

                          schön erklärt.

                          Auf jeden Fall vermisse ich den UPDATE auf die DB.

                          Ja habe ich auch schon bemerkt und nix hingeschrieben. Sorry 😕. War ja auch nur runterprogrammiert. Copy & Past im Editor musste ich dann erstmal etliche Syntaxfehler ausbügeln.

                          Ein "Requesthandler" ist ein Typ von Programm, der Requests bekommt und Antworten liefert.

                          Ok zählt Router dazu? Nah deiner Deninition schon wenn du "Subprogramme" dazu zählst

                          vlg MB

                          1. Hallo MB,

                            Ein "Requesthandler" ist ein Typ von Programm, der Requests bekommt und Antworten liefert.

                            Ok zählt Router dazu? Nah deiner Deninition schon wenn du "Subprogramme" dazu zählst

                            Äh. Hm. Tja. Keine Ahnung. Mein "Requesthandler" war eine Wortschöpfung von mir. Was verstehst Du unter Router? Ich assoziere damit (a) ein Gerät, das Teil der Netzwerkinfrastruktur ist und (b) eine Softwarekomponente, die Nachrichten auf Teilfunktionen der Software abbildet und dorthin delegiert (beispielsweise URLs auf Controller und Actions). Beides fällt nicht in diese grobe Kategorisierung von Software-Typen, die ich da aufgestellt habe. Höchstens könnte man den in (b) angeführten URL-Router als Teilkomponente eines Requesthandlers ansehen.

                            Ich schwafele deshalb so abstrakt daher, weil ein Router ein recht generischer Begriff ist. Grundsätzlich bekommt er Nachrichten, und leitet sie irgendwohin weiter. Möglicherweise bekommt er vom Weiterleitungsziel eine Antwort und leitet sie an den Anfragesteller weiter. Der Router an sich ist aber kein kurzlaufender Requesthandler, sondern ein langlaufender Service. Wobei es bei den Requesthandlern auch Mischformen zwischen kurz- und langlaufend geben kann: Ein PHP Script endet komplett, wenn der Request zu Ende ist, eine ASP.NET Anwendung wird einmal geladen und bleibt im Speicher - wenn die Klassen darin statische Elemente haben, kann man sich darin Werte von einem Request zum anderen merken (und sich eine blutige Nase holen wenn man nicht haarscharf aufpasst was man tut).

                            Beispiele für Komponenten mit Routing-Aufgaben:

                            • Dein Hoster hat einen Router, der die einlaufenden IP-Pakete auf die Server des Rechenzentrums verteilt. Dafür beachtet er IP, Port und ggf. muss er noch HTTP Header inspizieren (Verteilung per Hostname-Header).
                            • Der Server hat einen Router, der die einlaufenden IP Pakete nach Port trennt und der lauschenden Anwendung zuteilt (unter Windows ggf. wiedermal mit HTTP-Inspektion, da können mehrere Dienste auf den gleichen Port lauschen)
                            • Der http-Listener im Service "Apache" hat einen Router, je nach Hostname und Pfad das entsprechende Web adressiert. Ich weiß nicht wie der Apache strukturiert ist - aber der IIS kann in diesem Schritt den Request auf virtuelle Webs weiterleiten, die sich auf etliche Prozesse im Server verteilen und sehr unterschiedlich konfiguriert sind (Application Pools).
                            • Deine Website (oder mein RESTful WEB-API) hat einen Router, der aus der URL einen Controller und eine Action ableitet und die Arbeit dorthin delegiert.
                            • etc etc etc

                            Rolf

  2. Hi MB,

    Was ist DATA ACCESS LAYER?

    Ein DAL liegt zwischen Anwendung und physikalischen Speicher. hier noch einmal mein Beispiel vom Letztenmal:

    tie %{$self->{SESSION}}, 'SessionHash', file => $sid or die $@;
    
    # Zum Beispiel einen Login speichern
    # Credentials OK ab hier
    $self->{SESSION}{LOGINTAB} = {
      group => $GRUP,
      user  => $USER,
      ts    => time(),
    };
    
    # Daten persistent machen
    tied(%{$self->{SESSION}})->write;
    

    Übergeben beim Einbinden des DAL wird lediglich ein Dateiname, alternativ kann jedoch auch der Name einer Datenbank übergeben werden. Die Idee eines DAL besteht darin, den physikalischen Speicher von der Anwendung vollständig zu trennen. D.h., nur noch die Anwendung bestimmt den Aufbau abstrakter Datentypen und wenn diese persistent zu machen sind, übergibt die Anwendung die Daten an den DAL.

    In der Anwendung gibt es daher weder SQL-Statements noch irgendwelche Dateioperationen, mit der Übergabe der Daten an den DAL ist das Speichern aus der Sicht der Anwendung erledigt.

    Ein DAL ist damit austauschbar, ohne dass in der Anwendung CODE geändert werden muss.

    MfG

    PS: In den wenigen Zeilen CODE obenstehend findest Du übrigens noch zwei weitere Design Patterns. Da kannst Du Dich mal prüfen ob Du sie erkennst.