Heute war ich mit einem anderen Entwickler in meiner Organisation in einer hitzigen Debatte darüber, wo und wie Methoden zu Klassen mit Datenbankzuordnung hinzugefügt werden können. Wir verwenden sqlalchemy
, und ein Großteil der vorhandenen Codebasis in unseren Datenbankmodellen besteht aus kaum mehr als einer Menge zugeordneter Eigenschaften mit einem Klassennamen, einer nahezu mechanischen Übersetzung von Datenbanktabellen in Python-Objekte.
In dem Argument war meine Position, dass der Hauptwert der Verwendung eines ORM darin bestand, dass Sie den zugeordneten Klassen Verhalten und Algorithmen auf niedriger Ebene zuordnen können. Modelle sind erstens Klassen und zweitens beständig (sie könnten unter Verwendung von XML in einem Dateisystem beständig sein, Sie brauchen sich nicht darum zu kümmern). Seiner Ansicht nach ist jedes Verhalten "Geschäftslogik" und gehört notwendigerweise irgendwo anders als in das persistente Modell, das nur für die Datenbankpersistenz verwendet werden soll.
Ich bin sicher der Meinung, dass es einen Unterschied zwischen dem gibt, was Geschäftslogik ist, und dass dies getrennt werden sollte, da es eine gewisse Isolation von der unteren Ebene der Implementierung und der Domänenlogik gibt, die meiner Meinung nach die Abstraktion ist, die von den Modellklassen bereitgestellt wird Ich habe im vorigen Absatz darüber gestritten, aber es fällt mir schwer, den Finger auf das zu legen, was das ist. Ich habe ein besseres Gespür dafür, was die API sein könnte (was in unserem Fall HTTP "ReSTful" ist), indem Benutzer die API mit dem aufrufen, was sie tun möchten, und zwar unabhängig davon , was und wie sie tun dürfen fertig.
tl; dr: Welche Art von Dingen kann oder sollte in einer Methode in einer zugeordneten Klasse ablaufen, wenn ein ORM verwendet wird, und was sollte ausgelassen werden, um in einer anderen Abstraktionsebene zu leben?
quelle
Antworten:
Ich bin meistens bei dir; Ihr Kollege scheint entweder für das Antipattern des anämischen Domänenmodells zu streiten oder für das Duplizieren des Modells in ein "Persistenzmodell" ohne offensichtlichen Nutzen (ich arbeite an einem Java-Projekt, in dem dies getan wurde, und es bereitet massive Probleme mit der Wartbarkeit) es bedeutet das Dreifache der Arbeit, wenn sich etwas am Modell ändert.
Faustregel: Die Klasse sollte eine Logik enthalten, die grundlegende Fakten zu den Daten beschreibt, die unter allen Umständen wahr sind. Logik, die spezifisch für einen Anwendungsfall ist, sollte sich an einer anderen Stelle befinden. Ein Beispiel ist die Validierung. Es gibt einen interessanten Artikel von Martin Fowler, in dem er darauf hinweist, dass dies als kontextabhängig angesehen werden sollte.
quelle
Dies ist ein Urteilsspruch, der wirklich von Ihrer voraussichtlichen Größe und dem Umfang Ihrer Entwicklung abhängt. Der starrste Ansatz besteht darin, die ORM-Typen auf eine Datenzugriffskomponente zu beschränken und POCOs in einer gemeinsamen Bibliothek als Typen zu verwenden, die von allen Ebenen referenziert und verwendet werden. Dies würde eine zukünftige physische Trennung sowie eine logische Trennung ermöglichen. Sie können auch festlegen, dass eine zusätzliche Ebene zwischen der Benutzeroberfläche und der Geschäftslogikebene vorhanden sein soll. Dies wird normalerweise als Facade- oder Business Interface-Schicht bezeichnet. In dieser zusätzlichen Ebene befindet sich Ihr "Use-Case-Code". Der einzelne lose gekoppelte Code wird von der Facade / BI-Schicht aufgerufen (z. B. Facade hat eine ProcessOrder () -Funktion, die die Geschäftslogik 1: M-mal aufruft, um alle zur tatsächlichen Verarbeitung des Auftrags erforderlichen Schritte auszuführen).
Trotzdem: Vielfach ist diese Menge an Architektur einfach unnötiger Overkill. Codieren Sie beispielsweise speziell für eine einfache Website, auf der Sie die Komponenten nicht zur Wiederverwendung verpacken möchten. Es ist absolut richtig, eine MVC-Website zu erstellen und EF-Objekte für diese Art von Lösung zu verwenden. Wenn die Site später skaliert werden muss, können Sie sich mit Clustering oder einem häufig verlorenen Prozess namens "Refactoring" befassen.
quelle
Erinnern Sie Ihren Kollegen nur daran, dass Sie die Modelle nicht überarchitektonisch verändern müssen, als wäre dies ein Java-Projekt. Der Vergleich zweier persistenter Objekte ist ein Verhalten, das jedoch nicht durch die Persistenzschicht festgelegt wird. Die 6-Bier-Frage lautet also: Warum haben Klassen, die nichts miteinander zu tun haben, etwas über dasselbe beschrieben? Sicher, Beharrlichkeit ist ein wichtiger Aspekt eines Modells, der separat behandelt werden kann, aber nicht ausreichend, um zu gewährleisten, dass es unabhängig von allem anderen behandelt wird. Wenn Sie mit Ihrem Auto fahren, es waschen oder kaputt machen, manipulieren Sie Ihr Auto die ganze Zeit.
Warum also nicht einfach all diese verschiedenen Aspekte in einer einzigen Modellklasse zusammenfassen? Sie benötigen eine Reihe von Klassenmethoden, die sich mit dauerhaften Objekten befassen - ordnen Sie sie einer Klasse zu. Sie haben eine Reihe von Instanzmethoden, die sich mit der Validierung befassen - fügen Sie sie in eine andere ein. Zum Schluss mischen Sie die beiden in und voila! Sie haben sich genau dort eine intelligente, selbstbewusste und umfassende Modelldarstellung verschafft.
quelle
Achten Sie neben anderen Antworten auf versteckte Cavehats, wenn Sie Rich Domain-Modelle mit einem ORM verwenden.
Ich hatte Probleme beim Injizieren von polymorphen Diensten in die beständigen Modellklassen, als ich versuchte, so etwas wie den folgenden Pseudocode zu erzielen:
In diesem Fall kann eine Organisation
HRService
beispielsweise eine Abhängigkeit als Konstruktor benötigen . Normalerweise können Sie die Instanziierung Ihrer Modellklassen bei Verwendung eines ORM nicht einfach steuern.Ich habe Doctrine ORM und den Service-Container von Symfony verwendet. Ich musste den ORM auf eine weniger elegante Weise monkeypatchen und hatte keine andere Wahl, als Persistenz und Geschäftsmodelle zu trennen. Ich habe es noch nicht mit Sqlachemy versucht, dachte ich. Python könnte sich für dieses Zeug als flexibler als PHP erweisen.
quelle