Wer sollte die Bedingungen in komplexen Abfragen handhaben, der Data Mapper oder die Serviceschicht?

Lesezeit: 7 Minuten

Wer sollte die Bedingungen in komplexen Abfragen handhaben der Data
orourkek

Diese Frage hat sehr gute Arbeit geleistet, um meine Verwirrung in dieser Angelegenheit ein wenig zu beseitigen, aber es fällt mir schwer, zuverlässige Quellen für die genauen Einschränkungen der Serviceschicht zu finden.

Nehmen wir für dieses Beispiel an, dass wir es mit Büchern zu tun haben und Bücher nach Autor abrufen möchten. Die BookDataMapper könnte ein Generikum haben get() Methode, die Bedingungen wie die eindeutige Kennung des Buchs, den Namen des Autors usw. akzeptiert. Diese Implementierung ist ziemlich trivial (logisch), aber was ist, wenn wir mehrere Bedingungen haben möchten, die eine komplexere Abfrage erfordern?

Nehmen wir an, wir möchten alle Bücher, die von einem bestimmten Autor unter einem bestimmten Verlag geschrieben wurden, erhalten. Wir könnten die erweitern BookDataMapper->get() Methode, um mehrere Bedingungen zu parsen, oder wir könnten eine neue Methode schreiben, wie z BookDataMapper->getByAuthorAndPublisher().

Ist es vorzuziehen, dass die Serviceschicht diese aufruft? [more specific] Methoden direkt, oder lassen Sie die Bedingungen analysieren, bevor Sie die generischeren aufrufen BookDataMapper->get() Methode mit mehreren Bedingungen bestanden? Im letzteren Szenario würde die Serviceschicht mehr von der logischen „Schwerarbeit“ übernehmen, wodurch der Data Mapper ziemlich einfach bleibt. Die erstere Option würde die Serviceschicht fast vollständig auf einen Mittelsmann reduzieren und die bedingte Logik in Methoden wie dem Data Mapper überlassen BookDataMapper->getByAuthorAndPublisher().

Die offensichtliche Sorge, die Serviceschicht die Bedingungen parsen zu lassen, besteht darin, dass ein Teil der Domänenlogik aus dem Data Mapper austritt. (Dies wird in der verknüpften Frage hier erklärt. Wenn jedoch die Serviceschicht die Bedingungen handhaben würde, würde die Logik die Modellschicht nicht verlassen. Der Controller würde anrufen $book_service->getByAuthorAndPublisher() trotzdem.

1646320448 159 Wer sollte die Bedingungen in komplexen Abfragen handhaben der Data
teresko

Die Datenmapper Muster sagt Ihnen nur, was es tun soll, nicht wie es implementiert werden soll.
Daher sollten alle Antworten in diesem Thema als subjektiv behandelt werden, da sie die persönlichen Vorlieben jedes Autors widerspiegeln.

ich meistens Versuchen Sie, die Benutzeroberfläche des Mappers so einfach wie möglich zu halten:

  • fetch()ruft Daten im Domänenobjekt oder der Sammlung ab,
  • save()speichert (aktualisiert vorhandene oder fügt neue ein) das Domänenobjekt oder die Sammlung
  • remove()löscht das Domänenobjekt oder die Sammlung vom Speichermedium

Ich behalte die Bedingung im Domänenobjekt selbst:

$user = new User;
$user->setName( 'Jedediah' );

$mapper = new UserMapper;
$mapper->fetch( $user );

if ( $user->getFlags() > 5  )
{
    $user->setStatus( User::STATUS_LOCKED );
}

$mapper->save( $user );

Auf diese Weise können Sie mehrere Bedingungen für den Abruf haben und gleichzeitig die Schnittstelle sauber halten.

Der Nachteil dabei wäre, dass Sie eine öffentliche Methode zum Abrufen von Informationen aus dem Domänenobjekt benötigen, um eine solche zu haben fetch() Methode, aber Sie werden es trotzdem brauchen, um es auszuführen save().

Es gibt keine wirkliche Möglichkeit, das zu implementieren “Sag, frag nicht” Faustregel für die Interaktion zwischen Mapper und Domänenobjekten.

Wie für “Wie stellen Sie sicher, dass Sie das Domänenobjekt wirklich speichern müssen?”die Ihnen einfallen könnten, wurde hier behandelt, mit ausführlichen Codebeispielen und einigen nützlichen Bits in den Kommentaren.

Aktualisieren

Wenn Sie erwarten, mit Gruppen von Objekten umzugehen, sollten Sie es mit unterschiedlichen Strukturen statt mit einfachen zu tun haben Domänenobjekte.

$category = new Category;
$category->setTitle( 'privacy' );

$list = new ArticleCollection;

$list->setCondition( $category );
$list->setDateRange( mktime( 0, 0, 0, 12, 9, 2001) );
// it would make sense, if unset second value for range of dates 
// would default to NOW() in mapper

$mapper = new ArticleCollectionMapper;
$mapper->fetch( $list );

foreach ( $list as $article )
{
    $article->setFlag( Article::STATUS_REMOVED );
}

$mapper->store( $list );

In diesem Fall ist die Sammlung ein verherrlichtes Array mit der Fähigkeit, verschiedene Parameter zu akzeptieren, die dann als Bedingungen für den Mapper verwendet werden. Es sollte auch den Mapper zu einer erworbenen Liste führen geändert Domänenobjekte aus dieser Sammlung, wenn der Mapper versucht, die Sammlung zu speichern.

Der Mapper sollte in diesem Fall in der Lage sein, Abfragen mit allen zu erstellen (oder voreingestellte zu verwenden). möglich Bedingungen (als Entwickler kennen Sie alle diese Bedingungen, daher müssen Sie es nicht mit einer unendlichen Menge von Bedingungen zum Laufen bringen) und aktualisieren oder erstellen Sie neue Einträge für alle nicht gespeicherten Domänenobjekte, die diese Sammlung enthält.

Notiz: In mancher Hinsicht könnte man sagen, dass die Mapper mit Builder/Factory-Mustern verwandt sind. Das Ziel ist ein anderes, aber der Ansatz zur Lösung der Probleme ist sehr ähnlich.

  • Wie würde eine so einfache Direktnachricht beispielsweise damit umgehen, alle Benutzer zu erhalten, die sich zwischen zwei Terminen registriert haben? Wo würden die WHERE DB-Klauseln werden generiert?

    – orourkek

    13. August 2012 um 22:20 Uhr

  • In einer solchen Situation ordnen Sie kein einzelnes Domänenobjekt zu, sondern eine Sammlung davon. Eine Sammlung würde natürlich verschiedene Setter haben, dann ein einfaches Domänenobjekt, da sie so konzipiert ist, dass sie eine Gruppe von ihnen enthält.

    – teresko

    13. August 2012 um 22:29 Uhr


  • Nun, ein ähnliches Beispiel wäre, einen einzelnen Benutzer mit den gleichen Bedingungen und mit der gleichen angehängten Frage zu erhalten

    – orourkek

    13. August 2012 um 22:31 Uhr

  • Wenn Sie es mit einem Bereich zu tun haben, können Sie nicht davon ausgehen, dass er einen einzelnen Eintrag zurückgibt. Das bittet nur um Ärger.

    – teresko

    13. August 2012 um 22:37 Uhr

  • sehr wahr, die [non-programmatic] Abstraktion des Beispiels ist mir entgangen 😛

    – orourkek

    13. August 2012 um 22:41 Uhr

Normalerweise bevorzuge ich es, konkreter zu sein, wie:

BookDataMapper->getByAuthorAndPublisher($author, $publisher)

Das liegt daran, dass ich SQL nicht neu erfinden muss. Die Datenbank ist dafür besser geeignet und der Data-Mapper sorgt hier dafür, dass der Rest der Anwendung auch nichts darüber wissen muss, wie Dinge konkret gespeichert oder abgefragt werden.

Wenn Sie das dynamischer gestalten, können Sie leicht dazu neigen, zu viel Funktionalität über die Schnittstelle anzubieten. Nicht gut.

Und werfen Sie einen Blick auf Ihre Bewerbung. Sie werden sehen, dass nicht viel anders abgefragt werden muss. Für den Hauptteil der Daten sind das normalerweise etwa 5-10 Routinen, wenn überhaupt. Es ist viel schneller geschrieben, als auch nur an ein dynamisches System zu denken, das eigentlich sowieso in seine eigene Schicht gehören würde.

  • Interessant – ist dies nur eine Präferenz, oder gibt es einige Best Practices und/oder Dokumentationen, die dies unterstützen? Offensichtlich kann das Lecken von Logik/Funktionalität unter Best Practices bewertet werden, aber es könnte auch so kontrolliert und verfeinert werden, dass es überhaupt nicht aus der Modellschicht austritt, oder?

    – orourkek

    13. August 2012 um 22:08 Uhr

  • Puh, ich bin kein akademischer Entwickler, keine Ahnung, ob da dokumentierte Best Practices dahinterstecken. Wenn ich in der Rolle eines Verbrauchers irgendeiner Schicht bin – zB des Data-Mapper – habe ich gerne eine saubere und prägnante Schnittstelle, und das ist tatsächlich konkret. Kein versteckter Vertrag über mehr oder weniger dynamische Wert- oder Parameterobjekte. Ich meine, ansonsten kann ich einfach etwas SQL ausführen, wenn ich mehr Flexibilität benötige. Aber das würde die Schicht zerstören. Also eine Grenze ziehen und gut damit sein.

    – hakre

    13. August 2012 um 22:20 Uhr


  • Ich möchte hinzufügen: Einige Implementierungen haben Finder neben Mappern. Diese kümmern sich darum, Objekte zu finden, Mapper kümmern sich immer noch darum, die Daten zu kartieren. Manchmal können Sie diese zusammenstellen, manchmal ist es besser, sie getrennt zu haben, damit Sie sie erweitern können, wenn die Anzahl der Finder wächst.

    – hakre

    13. August 2012 um 22:24 Uhr


  • Ich ziehe diese Antwort der akzeptierten vor. Es passt eher zum Repository-Ansatz, der sicherstellt, dass eine solche Suchlogik im Geschäftsbereich relevant ist – z. B. die Suche nach Büchern nach Autor und Verlag wie im gezeigten Beispiel. Solche Methoden sollten von der Repository-API (oder der Data-Mapper-API, wenn Sie so wollen) bereitgestellt werden. Dies macht die Suchlogik offensichtlicher und verhindert, dass sie über verschiedene andere Klassen (z. B. Controller) verstreut ist.

    – äh

    25. August 2020 um 6:59 Uhr

924670cookie-checkWer sollte die Bedingungen in komplexen Abfragen handhaben, der Data Mapper oder die Serviceschicht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy