Umgang mit komplex berechneten Feldern in einem ORM

8

In unserer API gibt es einige zentrale Datentypen, die nach dem Abrufen aus der Datenbank mit berechneten Werten (sozusagen) "dekoriert" werden müssen. Der Zugriff auf die Datenbank erfolgt über ein ORM, das einer stark von der CakePHP 3-Datenbankebene inspirierten Tabellen- / Entitätsdynamik folgt, bei der ein Tabellenobjekt als Vermittler zwischen der Datenbank und der Anwendung verwendet wird, die Zeilen als Modellobjektinstanzen ein- und ausgibt. Anstatt nur Daten aus der Datenbank abzurufen und diese Zeilen zurückzugeben, müssen wir die zurückgegebenen Daten vorverarbeiten, bevor sie tatsächlich verwendet werden können. Hier sind einige Anwendungsfälle, die aufgetaucht sind, um besser zu erklären, was ich meine:

  • Objekte haben numerische Werte, die in benutzerfreundliche Bezeichnungen übersetzt werden (normalerweise ist dies eine Logik, die ich nur auf dem Client behalten würde, aber aus Gründen der Geschäftssicherheit müssen einige dieser Daten nur auf dem Server gespeichert werden, zugegebenermaßen ein bisschen Randfall)
  • Objekten muss ein Bewertungswert zugeordnet sein, der aus der zuletzt hinzugefügten Bewertung gezogen wird
  • Basierend auf einer Kombination aus solchen berechneten Werten und gespeicherten Werten wird ein komplexes Zeitplanobjekt erstellt

Alle diese Elemente sind für sich genommen mit einer einfachen map()Operation über die zurückgegebene Ergebnismenge ziemlich einfach durchzuführen. Das Gleiche gilt, wenn Sie mehrere berechnete Werte wünschen. Sie können einfach mehr Kartenoperationen ausführen, um diese Felder nach Bedarf zu berechnen und hinzuzufügen.

Dieser Ansatz weist jedoch zwei Hauptnachteile auf:

  1. Dies bedeutet, dass Sie überall dort, wo Sie mit diesen berechneten Werten arbeiten möchten, einen zusätzlichen Schritt der Nachbearbeitung ausführen müssen, was nicht besonders trocken ist
  2. Einige dieser Transformationen hängen davon ab, dass andere Transformationen zuerst durchgeführt werden, andernfalls stehen ihnen einfach nicht die Daten zur Verfügung, mit denen sie arbeiten können

Um beides zu handhaben, habe ich mir überlegt, dass der beste Ansatz darin besteht, diesen Code in das ORM zu verschieben und dann das ORM so zu ändern, dass die Schnittstelle (extern) den Zugriff auf die berechneten virtuellen Felder auf die gleiche Weise ermöglicht, wie sie mit Datenbankspalten umgeht . Intern könnte es diese virtuellen Felder dann Transformationsfunktionen zuordnen und intern alle potenziell erforderlichen Abhängigkeitstransformationen bestimmen, um das zweite Problem zu lösen.

(Abgesehen davon frage ich mich, ob dies auch die Notwendigkeit beseitigt, dass die zurückgegebenen Zeilen tatsächliche Objekte sind, im Gegensatz zu einfachen Hashes. Im Moment instanziiert jede Zeile ein neues Objekt mit dem Felddatensatz darauf, aber wenn alle Berechnungen oder Wenn Änderungen an den Daten aus dem Modell verschoben werden, wird das Objekt zu einer Tasche mit Eigenschaften - eine Hashmap im Wesentlichen ohne eigene interne Logik. Was meiner Meinung nach möglicherweise keine schlechte Sache ist.

Moberemk
quelle
Wie ist das @moberemk gelaufen?
Slamice
Wenn es mit ORM zu schwierig ist, können Sie eine native SQL-Abfrage verwenden. Normalerweise bietet ORM die Möglichkeit, sichere SQL-Abfragen durchzuführen, da sie genau wissen, dass sie nicht jede Situation bewältigen können, die in Raw-SQL möglich ist.
Walfrat

Antworten:

3

Sie können für die oben genannten Fälle eine Repository-ähnliche Ebene verwenden.

[Repository] Vermittelt zwischen der Domänen- und der Datenzuordnungsschicht über eine sammlungsähnliche Schnittstelle für den Zugriff auf Domänenobjekte.

Ein Repository pro Fall, das ORM zum Lesen von Daten verwendet, diese anreichert und zurückgibt.

Sie hätten also eine einheitliche Möglichkeit, auf solche Instanzen zuzugreifen und zu verbergen, wie diese Instanzen von außen erstellt werden. Auf diese Weise können Sie auch von ORM zu unformatierten SQL-Abfragen wechseln, ohne die exponierte Schnittstelle zu ändern.

Potfur
quelle
Dies ist, was ich letztendlich für dieses Projekt getan habe, aber ich möchte darauf hinweisen, dass es bei großen Datenmengen massive Leistungsprobleme gab. Im Wesentlichen: Es funktioniert, nur nicht im Maßstab. Akzeptieren für die Genauigkeit.
Moberemk
0

Ich stimme @potfur zu. Die Aufteilung zwischen den "Datenobjekten", die Daten in der Datenbank darstellen, und ihrer "Geschäfts" -Darstellung, die zusätzliche Logik, Berechnung usw. einschließt, ist meiner Meinung nach die richtige Richtung. Wie die Daten für eine bestimmte Domain / ein bestimmtes Unternehmen dargestellt werden und was dann technisch gespeichert wird, kann völlig anders sein. Die Implementierung der Geschäftslogik mit Objekten, die die Domäne darstellen, trägt zur Wertsteigerung für den Kunden bei und erleichtert die Kommunikation. Beim ORM erwähnen Sie Skalierbarkeitsprobleme. Ich denke, ein ORM ist ein Anti-Muster. Obwohl es im kleineren / mittleren Maßstab sehr nützlich ist, beginnt es in Bezug auf die Skalierbarkeit zu scheitern. Sie können eine Caching-Ebene für die "Geschäftsentitäten" hinzufügen, damit Sie sie nicht jedes Mal berechnen müssen.

David Lukac
quelle