Business Objects - Container oder funktional?


19

Dies ist eine Frage, die ich vor einiger Zeit auf SO gestellt habe, aber hier wird sie vielleicht besser diskutiert ...

Wo ich arbeite, sind wir zu diesem Thema einige Male hin und her gegangen und haben nach einer Überprüfung der geistigen Gesundheit gesucht. Hier ist die Frage: Sollten Business-Objekte Datencontainer sein (eher DTOs ) oder sollten sie auch Logik enthalten, die einige Funktionen für dieses Objekt ausführen kann.

Beispiel - Nehmen Sie ein Kundenobjekt, das wahrscheinlich einige allgemeine Eigenschaften enthält (Name, ID usw.). Sollte dieses Kundenobjekt auch Funktionen enthalten (Speichern, Berechnen usw.)?

Eine Argumentation besagt, dass das Objekt von der Funktionalität (Prinzipal mit einer Verantwortlichkeit) getrennt und die Funktionalität in eine Business-Logik-Ebene oder ein Objekt eingefügt wird.

Die andere Argumentation besagt: Nein, wenn ich ein Kundenobjekt habe, möchte ich nur Customer.Save anrufen und fertig sein. Warum muss ich etwas über eine andere Klasse wissen, um einen Kunden zu retten, wenn ich das Objekt konsumiere?

Bei unseren letzten beiden Projekten wurden die Objekte von der Funktionalität getrennt, aber die Debatte wurde erneut über ein neues Projekt geführt.

Was macht mehr Sinn und warum?


1
Können Sie uns mehr über Ihr Projekt erzählen? Die Art Ihres Projekts bestimmt, welche Lösung besser ist.

Antworten:


5

Wenn Sie der Ansicht sind, dass ein Kunde Teil des Domänenmodells ist, ist es sinnvoll (insbesondere im Kontext von DDD, aber nicht darauf beschränkt), sowohl Eigenschaften als auch Operationen für dieses Objekt zu haben.

Trotzdem denke ich, dass das Beispiel, das Sie verwendet haben, schlecht ist und eine Ursache für die Auseinandersetzung ist. Wenn Sie von Beharrlichkeit sprechen, dann "sparen" sich Kunden im Allgemeinen nicht. was auch immer Sie für Ausdauer verwenden, wird. Es ist sinnvoll, dass jede Art von Persistenz zur Persistenzschicht / -partition gehört. Dies ist im Allgemeinen die Überlegung hinter dem Repository-Muster. ** Eine Methode wie Customer.UpgradeWarranty () oder Customer.PromoteToPreferred () macht das Argument klarer.

Dies hebt auch nicht die Möglichkeit auf, DTOs zu haben . Betrachten Sie beispielsweise die Situation, in der Sie Kundeninformationen an einen Remote-Service weitergeben. Es mag für einen Kunden nicht sinnvoll sein, ein DTO für den Transport selbst zu erstellen, das ist ein architektonisches Problem, aber es könnte ein Fall für die Persistenz oder die Netzwerkschicht / Partition / Code / What-Have-You sein. In diesem Fall könnte ein solches Objekt Methoden haben, die so aussehen

public static CustomerDTO GetDTO(Customer c) { /* ... */ }

public static Customer GetCustomer(CustomerDTO cdto) { /* ... */ }

Zusammenfassend ist es also durchaus sinnvoll, Operationen an einem Domänenobjekt durchzuführen, die mit logischen Operationen in der Domäne übereinstimmen.

Google for 'Persistence Ignorance' für eine Reihe guter Diskussionen zu diesem Thema ( diese SO-Frage und ihre akzeptierte Antwort sind ein guter Anfang).

** Dies wird durch bestimmte OR / M-Software etwas unübersichtlich, da Sie gezwungen sind, von einer persistenten Basis zu erben, die über eine "Save" -Methode verfügt.


3

Ich habe kürzlich einen Code überarbeitet, um die Objekte von den Daten zu trennen. Der Grund dafür ist, dass die Daten über einen separaten Dienst abgerufen werden und es viel einfacher ist, die Rohdaten nur zum / vom Server zu übertragen, als das gesamte Objekt hin und her zu übertragen und Validierungsfehler auf dem Server zu beheben.

Bei kleinen Projekten ist es wahrscheinlich in Ordnung, Objekte mit ihrer Geschäftslogik und ihren Daten zu versehen. Bei großen Projekten oder Projekten, die sich im Laufe der Zeit erweitern, würde ich die Ebenen jedoch definitiv trennen.


3

Ich denke, dass beide Methoden ihre Vorteile haben können, aber ich würde es aus einer objektorientierten oder domänengetriebenen Sicht betrachten: Was sind die Verantwortlichkeiten der verschiedenen Klassen und Objekte.

Ich würde mich also in Ihrem konkreten Fall fragen: Sollte ein Kunde wissen, wie er sich selbst retten kann?

Für mich wäre die Antwort nein: Logischerweise macht es für mich keinen Sinn, und ein Kunde sollte überhaupt nichts über ein Persistenz-Framework wissen müssen (Aufteilung der Verantwortlichkeiten).

Die Beispiele von SnOrfus mit Customer.UpgradeWarranty () oder Customer.PromoteToPreferred () sind offensichtlich stärker auf Geschäftslogik ausgerichtet als Customer.Save (). Hierfür gibt es verschiedene Ansätze. Wenn Sie sich jedoch erneut fragen, ob ein Kunde in der Lage sein soll, seine Garantie zu aktualisieren, lautet die Antwort möglicherweise Ja oder Nein, je nachdem, wie Sie es betrachten:

  • ja: ein kunde kann natürlich seine garantie upgraden
  • nein: Ein Kunde könnte fragen , aufgerüstet werden, aber das Upgrade von jemand anderem gemacht wird

Zurück zu Ihrer ursprünglichen Frage; Welcher Weg sinnvoller ist, hängt wahrscheinlich davon ab, wen Sie fragen und welche Methode Sie bevorzugen. Das Wichtigste ist jedoch wahrscheinlich, sich an einen Weg zu halten.

Nach meiner Erfahrung führt die Trennung von Daten und (Geschäfts-) Logik zu einer einfacheren Architektur, wenn auch nicht so aufregend.


2

Ich denke, Sie sprechen speziell über den Unterschied zwischen dem ActiveRecordMuster und dem RepositoryMuster. Im ersten Fall wissen die Entitäten, wie sie sich selbst behaupten sollen, und im zweiten Fall weiß das Repository über Persistenz Bescheid. Ich denke, letzteres bietet eine bessere Trennung der Anliegen.

Im weiteren Sinne sollten die Entitäten, wenn sie sich eher wie eine Datenstruktur verhalten, keine Verhaltensweisen aufweisen, aber wenn sie Verhaltensweisen aufweisen, sollten sie nicht wie eine Datenstruktur verwendet werden. Beispiel:

Datenstruktur:

class CustomerController
{
    public int MostRecentOrderLines(Customer customer)
    {
        var o = (from order in customer.Orders
                orderby order.OrderDate desc
                select order).First();
        return o.OrderLines.ToList().Count;
    }
}

Nicht-Datenstruktur:

class Customer
{
    public int MostRecentOrderLines()
    {
        // ... same ...
    }
}

Im ersten Fall können Sie problemlos im Datenstrukturbaum Ihres Modells von überall in Ihrem Arbeitscode navigieren, und das ist in Ordnung, da Sie es wie eine Datenstruktur behandeln. Im letzteren Fall ist der Kunde eher eine Dienstleistung für einen bestimmten Kunden, weshalb Sie keine Methoden für das Ergebnis aufrufen sollten. Alle Informationen, die Sie über den Kunden wissen möchten, sollten verfügbar sein, indem Sie eine Methode für das Kundenobjekt aufrufen.

Wollen Sie also, dass Ihre Entitäten Datenstrukturen oder Dienste sind? Aus Gründen der Konsistenz scheint es besser, bei einem zu bleiben. Im ersten Fall platzieren Sie Ihre "Service" -Logik an einer anderen Stelle, nicht in der Entität.


1

Ich kann Ihre Frage nicht wirklich beantworten, aber ich finde es lustig, dass wir diese Diskussion auch in einem unserer Projekte in der Schule führen. Ich möchte die Logik von den Daten trennen. Aber viele meiner Klassenkameraden sagen, dass das Objekt alle logischen UND-Daten enthalten muss.

Ich werde versuchen, die Fakten zusammenzufassen:

  • Ein Business-Class-Objekt stellt eine Sache dar, daher sollten alle Daten und Logik enthalten sein. (zB wenn du eine Tür öffnen willst, machst du es selbst, fragst nicht jemand anderen. schlechtes Beispiel, das ich kenne)

  • Einfacheres Verstehen des Codes und der Funktionalität eines bu-Objekts.

  • Geringere Komplexität im Design

Ich sage ihnen, dass sie faul sind und ich würde es so machen:

  • Ja, Business Classes repräsentieren Dinge, also enthalten sie Daten. Aber es fühlt sich falsch an, sich selbst zu retten oder sogar zu kopieren. Sie können das nicht in rl tun.

  • Indem das Objekt für alles verantwortlich gemacht wird, ist es für die Haltbarkeit und Wartbarkeit in der Zukunft nicht mehr gut. Wenn Sie eine separate Klasse haben, die für das Speichern verantwortlich ist, können Sie die Speicherfunktion einfach implementieren, wenn Sie dem Entwurf ein neues Objekt hinzufügen. anstatt alles noch einmal in dieser Klasse zu codieren.

  • Wenn ein Objekt Daten persistieren kann, kann dieses Objekt eine Datenbankverbindung halten, und der gesamte Datenbankverkehr wird an diesem Objekt geleitet. (Grundsätzlich ist es die Datenzugriffsebene.) Andernfalls müsste das gesamte Business-Objekt eine Verbindung halten. (was die Komplexität erhöht)

Auf die eine oder andere Weise werde ich einen Lehrer fragen. Wenn ich das getan habe, werde ich seine Antwort auch hier posten, wenn Sie möchten. ;)

bearbeiten:

Ich habe vergessen zu erwähnen, dass es sich bei diesem Projekt um einen Buchladen handelt.


Ihre Klassenkameraden haben Recht, außer dass sie Geschäftslogik mit anderen Formen von Logik verwechseln (in diesem Fall Architektur- oder Persistenzlogik).
Steven Evers

1

Die Antwort wird wirklich davon abhängen, wie Ihre Architektur / Ihr Design aussieht - eine DDD-Architektur mit einem Domänenmodell wird sich in Bezug auf das Objektdesign sehr von einem datenorientierten CRUD-Modell unterscheiden.

Denken Sie jedoch im Allgemeinen daran, dass Sie im objektorientierten Design versuchen, den Zustand einzuschließen und das Verhalten aufzudecken. Dies bedeutet, dass Sie normalerweise versuchen, den Status, der mit einem Verhalten verknüpft ist, so nah wie möglich an dieses Verhalten heranzuführen. Wenn Sie dies nicht tun, müssen Sie den Status in der einen oder anderen Form offen legen.

In einem Domain-Modell möchten Sie das Geschäftsmodell von den Infrastrukturbelangen trennen. Daher sollten Sie unbedingt Methoden wie ".Save" vermeiden. Ich kann mich nicht erinnern, wann ich das letzte Mal einen Kunden im wirklichen Leben "gerettet" habe!

In einer CRUD-Anwendung sind die Daten dann Bürger erster Klasse, sodass ein ".Save" durchaus angemessen ist.

Die meisten großen realen Anwendungen werden eine Mischung dieser Paradigmen aufweisen - Domänenmodelle, bei denen sich die Geschäftsregeln schnell ändern oder komplex sind, DTOs für die grenzüberschreitende Datenübertragung, CRUD (oder etwas zwischen CRUD und einem Domänenmodell, wie Activerecord) an anderer Stelle. Es gibt keine Einheitsregel.


0

Ich habe über die gleiche Frage eine Weile nachgedacht - ich denke, die Datenkomponente und die Logikkomponente müssen getrennt sein. Ich glaube das, weil es Sie in die richtige Stimmung versetzt, wenn es darum geht, Geschäftslogik als Schnittstelle zu den Daten zu betrachten, die Sinn ergeben.

Ich würde auch Scott Whitlocks Punkt von oben modifizieren (außer ich habe keine Punkte als neues Mitglied), Daten- oder Geschäftslogikklassen sollten sich nicht wirklich darum kümmern müssen, wie das Objekt dauerhaft gespeichert wird.

Davon abgesehen, wenn Sie mit einem vorhandenen Produkt zu tun haben, solange Sie saubere, vertragliche Schnittstellen haben - das ist in Ordnung und auch wartbar ...

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.