Warum die Geschäftslogik in das Modell aufnehmen? Was passiert, wenn ich mehrere Speichertypen habe?

70

Ich dachte immer, dass die Geschäftslogik im Controller sein muss und dass der Controller, da er der „mittlere“ Teil ist, statisch bleibt und dass das Modell / die Ansicht über Schnittstellen gekapselt werden muss. Auf diese Weise können Sie die Geschäftslogik ändern, ohne etwas anderes zu beeinflussen, mehrere Modelle (eines für jede Datenbank / jeden Speichertyp) und Dutzende von Ansichten (zum Beispiel für verschiedene Plattformen) programmieren.

Jetzt habe ich in dieser Frage gelesen , dass Sie immer die Geschäftslogik in das Modell einfügen sollten und dass der Controller tief mit der Ansicht verbunden ist.

Für mich ist das nicht wirklich sinnvoll und impliziert, dass ich jedes Mal, wenn ich die Möglichkeit haben möchte, eine andere Datenbank / einen anderen Speichertyp zu unterstützen, mein gesamtes Modell einschließlich der Geschäftslogik neu schreiben muss.

Und wenn ich eine andere Ansicht haben möchte, muss ich sowohl die Ansicht als auch den Controller neu schreiben.

Kann jemand erklären, warum das so ist oder ob ich irgendwo einen Fehler gemacht habe?

Steffen Winkler
quelle

Antworten:

69

ElYusubovs Antwort nagelt es meistens, Domänenlogik sollte in das Modell und Anwendungslogik in den Controller gehen.

Zwei Klarstellungen:

  • Der Begriff Geschäftslogik ist hier eher unbrauchbar, weil er mehrdeutig ist. Geschäftslogik ist ein Oberbegriff für alle Logik, die Geschäftsleute interessieren, und trennt sie von bloßen technischen Aspekten wie dem Speichern von Inhalten in einer Datenbank oder dem Rendern auf einem Bildschirm. Sowohl die Domänenlogik ("Eine gültige E - Mail - Adresse sieht aus wie ...") als auch die Arbeitsabläufe / Geschäftsprozesse ("Wenn sich ein Benutzer anmeldet, fragen Sie nach seiner E - Mail - Adresse") werden als Geschäftslogik betrachtet Modell und das letztere ist Anwendungslogik, die in den Controller geht.
  • MVC ist ein Muster zum Platzieren von Inhalten auf einem Bildschirm und zum Ermöglichen der Interaktion mit dem Benutzer. Es gibt überhaupt keinen Speicher an . Die meisten MVC-Frameworks sind Full-Stack-Frameworks, die über MVC hinausgehen und Ihnen beim Speichern Ihrer Daten helfen. Da sich die zu speichernden Daten normalerweise im Modell befinden, bieten diese Frameworks praktische Möglichkeiten zum Speichern Ihrer Modelldaten. Daten in einer Datenbank, aber das hat nichts mit MVC zu tun. Im Idealfall sollten Modelle persistenzunabhängig sein und der Wechsel zu einem anderen Speichertyp sollte sich überhaupt nicht auf den Modellcode auswirken. Vollwertige Architekturen haben eine Persistenzschicht, um dies zu handhaben.
Waquo
quelle
4
Die meisten MVC-Frameworks mischen das gesamte Speicher- / Datenbankmaterial in das Modell, um das Speichern Ihrer Modelle zu vereinfachen (häufig durch Erweitern der Framework-Modellklasse). Dies ist wahrscheinlich die Quelle der Verwirrung. Technisch gesehen sollte der von Ihnen geschriebene Modellcode das tatsächliche Modell (Domänenschicht) sein, wohingegen der vom Framework bereitgestellte Code die Speicherung (Persistenzschicht) behandeln sollte. Zum Beispiel funktioniert so etwas wie User.find (...) (wobei User ein Modell ist), weil das Framework das Repository-Muster als Teil des Modells implementiert hat.
Waquo
3
View-Controller-Model-Storage ist das allgemeine Prinzip (obwohl die Beziehung zwischen M, V und C als Dreieck dargestellt werden sollte). Wenn Ihr Framework Speicher in sein "Modell" mischt, funktioniert dies folgendermaßen: View-Controller- (Modell erbt Speicher vom Framework).
Waquo
2
View-Controller-Model-Storage ist eher grob, weil es nicht flach sein sollte. Wenn ein Controller beispielsweise so etwas wie User.find (...) ausführt, um ein Modell zu erhalten, fragt er die Speicherebene direkt, anstatt die Domänenebene zu durchlaufen.
Waquo
2
In Architekturen mit vorsichtigerer Schichtung wäre dies etwa UserRepository.find (). Mit "model" habe ich die "model" -Klasse gemeint, die von dem Framework bereitgestellt wird, von dem Sie erben. Das von User.find () zurückgegebene User-Objekt ist ein Modell eines Benutzers in dem Sinne, dass jemand modelliert, was ein Benutzer ist, wie sich ein Benutzer verhält ...
Waquo
1
@flipdoubt Geschäftslogik ist jede Logik, die gleich bleiben sollte, wenn Sie von MVC portiert haben, um eine UWP-App zu nennen.
Andy
23

Sie und große Teile der Programmierwelt scheinen die Rollen der MVC-Teile falsch zu verstehen. Kurz gesagt sind sie:

Modell = Domänenlogik

Ansicht = Ausgangslogik

Controller = Eingangslogik

Dies bedeutet, dass das Modell für die gesamte Geschäftslogik verantwortlich ist: Alles, was mit dem Zeichnen von Widgets auf einem Bildschirm, dem Ansteuern eines Druckers, dem Ausgeben von Daten als HTML, dem Parsen von HTTP-Anforderungen usw. zusammenhängt, gehört nicht zum Modell.

Viele der modernen sogenannten "MVC" -Frameworks machen MVC jedoch überhaupt nicht oder sie kennzeichnen ihre Teile falsch. Was als "Modell" bezeichnet wird, ist häufig die Persistenzschicht des Modells, während sich die Geschäftslogik in dem befindet, was sie als "Controller" bezeichnen. Der eigentliche Controller ist in der Regel nur ein zentraler Einstiegspunkt mit einer Routing-Tabelle und einer Codestelle in den einzelnen Controllern, um die eingegangenen Eingaben an die richtigen Geschäftsprozesse weiterzuleiten. Was diese Frameworks "Ansicht" nennen, ist wirklich ein bisschen von allem: etwas Präsentationslogik (Ansicht), ein bisschen Eingabehandhabung und Validierung (Controller) und etwas mehr Geschäftslogik (Modell). Der Löwenanteil der aktuellen Ansicht wird normalerweise als "Vorlagen" bezeichnet.

Vielleicht möchten Sie auch etwas über die Multi-Tier-Architektur lesen. Wo MVC eine Art von Einbahnstraße ist (der Fluss ist Controller -> Modell -> Ansicht), ist Multi-Tier eine Zwei-Wege-Sache (Präsentation -> Logik -> Daten -> Logik -> Präsentation) und ziemlich viele Frameworks, die so tun, als ob sie MVC ausführen, sind dreistufig und kennzeichnen Presentation für View, Logic für Controller und Data für Model neu.

tdammers
quelle
2
Ich glaube, Sie haben das Modell falsch dargestellt ("Modell = Domänenlogik"). Meines Erachtens handelt es sich eher um ein Gefäß für Populationen mit Daten, die dann mithilfe einer Ansicht in der reinsten Form des MVC-Musters gerendert werden. Sicher, in wirklich einfachen Anwendungsfällen können Sie es als "Domänenlogik" behandeln, aber ich würde dafür bürgen, dass die meisten Systeme, die es wert sind, vorhanden zu sein, so schnell herauswachsen würden. Besser ist es, die "Domänenlogik" in eine separate Schicht / Klasse aufzuteilen, z. B. eine Dienstschicht.
A. Murray
@ A.Murray: Natürlich muss das Modell kein monolithischer Code-Block sein, und eine Aufteilung in Persistenz, Datenstrukturen und Domänenlogik ist normalerweise sehr sinnvoll. Dennoch fasst MVC diese drei Anliegen im Modell zusammen. Wenn Ihre Controller und Ansichten Domänenlogik enthalten, ist dies auf jeden Fall keine echte MVC mehr.
tdammers
@tdammers, ich mag die Ordnung Ihrer Antwort und den Fokus auf Logik. Wohin gehören aus Ihrer Sicht Anwendungsprobleme wie Persistenz und Transaktionsverarbeitung? Scheint so, als ob MVC ein Akronym mit vier Buchstaben sein sollte, wie MVCS, wo das S für den Dienst ist.
Flipdoubt
15

Um Geschäftslogik wirklich zu isolieren und sie von der Infrastruktur der Präsentationsschicht zu trennen, sollte sie von Anwendungsdiensten gekapselt werden. Die MVC-Architektur ist eine Möglichkeit, die Präsentationsschicht zu implementieren. Sie sollte in diesem Bereich bleiben und die gesamte Geschäftslogik an diese Anwendungsdienste delegieren. Stellen Sie sich Ansichtsmodelle als Adapter zwischen der Ansicht und den Daten vor, die angezeigt und / oder gelesen werden müssen. Der Controller vermittelt die Interaktion zwischen Ansichtsmodellen, Ansichten und Anwendungsdiensten, die die Geschäftslogik hosten.

Anwendungsdienste implementieren Geschäftsanwendungsfälle und sind von der Präsentationsschicht entkoppelt, sei es MVC oder etwas anderes. Anwendungsdienste können wiederum Transaktionsskripts oder ein domänengesteuertes Design hosten .

Zum Speichern kann der Anwendungsdienst auf ein Repository oder eine beliebige Abstraktion eines Persistenzmechanismus verweisen . Verschiedene Implementierungen können unterstützt werden, indem der Datenzugriff in eine Schnittstelle abstrahiert wird. In der Regel sind diese Abstraktionen undicht und können nur teilweise über Implementierungen hinweg portiert werden. Oft ist es ein vergeblicher Versuch, eine vollständige Portabilität zu erreichen.

AKTUALISIEREN

Mein Vorschlag basiert auf der hexagonalen Architektur . In einer hexagonalen Architektur steht Ihr Domänenmodell (Geschäftslogik) im Mittelpunkt. Dieser Kern wird von Anwendungsdiensten gekapselt, die als Fassade fungieren . Anwendungsdienste sind einfache Klassen, deren Methoden den Anwendungsfällen in Ihrer Domäne entsprechen. Ausführliche Informationen zu Anwendungsdiensten finden Sie unter Dienste im domänengesteuerten Design . Das Codebeispiel enthält einen PurchaseOrderServiceAnwendungsdienst für eine Einkaufsdomäne. (Beachten Sie, dass ein Anwendungsdienst nicht die Verwendung eines domänengesteuerten Designs impliziert.)

In einer hexagonalen Architektur ist eine MVC-Präsentationsschicht ein Adapter zwischen Ihrem Domänenmodell (Geschäftslogik) und einer GUI. Das Domänenmodell kennt die Präsentationsschicht nicht, aber die Präsentationsschicht kennt das Domänenmodell.

Diese Lösung hat sicherlich bewegliche Teile als eine Lösung, die Geschäftslogik in die Steuerung einfügt, und Sie sollten die Nachteile und Vorteile abwägen. Der Grund, warum ich dies vorschlage, liegt darin, dass ich es bevorzuge, die Geschäftslogik von der Präsentationsebene zu entkoppeln, um der Komplexität entgegenzuwirken. Dies wird wichtiger, wenn die Anwendung wächst.

eulerfx
quelle
Klingt so, als würdest du einen Bastard von MVC und MVVM beschreiben, der sowohl Controller- als auch View-Modelle hat. Ich denke auch, dass die Architektur, die Sie beschreiben, für die Anforderungen von OP etwas schwer sein kann.
Waquo
um ehrlich zu sein, ich mag Waquos Antwort mehr. Hauptsächlich, weil ich keine Ahnung habe, was Sie mit "Anwendungsdiensten" meinen. Könnten Sie diesen Begriff erklären? Meine GoogleFU funktioniert hier anscheinend nicht.
Steffen Winkler
1
@Waquo Ich bin damit einverstanden, dass die vorgeschlagene Architektur übertrieben sein kann, sollte jedoch in Betracht gezogen werden. Ich habe MVVM nicht erwähnt, was einfach eine andere Möglichkeit ist, eine Präsentationsebene zu implementieren. Anwendungsdienste gelten unabhängig davon, ob Sie MVC oder MVVM verwenden, und nichts, was ich vorgeschlagen habe, weist auf eine Kombination der beiden hin.
Eulerfx
1

Kommt darauf an, was Sie unter Geschäftslogik verstehen. Jede "Logik", die den Inhalten des Modells Bedeutung verleiht, sollte im Modell enthalten sein. In der verknüpften Frage scheint die Antwort mit der höchsten Bewertung "Geschäftslogik" als etwas zu definieren, das sich auf Daten bezieht. Das macht Sinn, wenn man bedenkt, dass die Daten eines Unternehmens sein Geschäft sind!

Ich habe einmal ein Beispiel von dem Schöpfer von Rails gesehen (glaube ich), der genau das tat - ohne "Geschäftslogik" in das Modell aufzunehmen. Sein Beispiel war eine Controller-Klasse und Methode für die Registrierung und Anmeldung von Apps. Ein im Klartext angegebenes Kennwort wurde verschlüsselt, bevor es in das Modell (eine Datenbank) eingefügt oder von diesem abgefragt wurde.

Ich kann mir kein besseres Beispiel für etwas vorstellen, das keine Steuerungslogik ist und direkt zum Modell gehört.

Das Modell könnte eine Schnittstelle zu unzähligen Datenspeichern sein und die Portabilität beeinträchtigen. Hier könnte man Verwirrung darüber finden, ob die Modellschnittstelle tatsächlich der "Controller" ist oder nicht.

Im Allgemeinen verknüpft der Controller das Modell und die Ansicht (die das Fleisch und die Kartoffeln der App sind). In der Cocoa-Entwicklung kann es so einfach sein, dass der Controller über die XCode-GUI (Controller-Objekte und -Bindungen) verwaltet wird.

Der GoF-Abschnitt "Design Patterns" auf MVC, lose zitiert:

Die MVC-Klassentriade wird zum Erstellen von Benutzeroberflächen in Smalltalk-80 verwendet. Das Modell ist das Anwendungsobjekt, die Ansicht die Bildschirmdarstellung und der Controller definiert, wie die Benutzeroberfläche auf Benutzereingaben reagiert. MVC entkoppelt Ansichten und Modelle, indem ein Abonnement- / Benachrichtigungsprotokoll zwischen ihnen erstellt wird. Das folgende Diagramm zeigt ein Modell und drei Ansichten. Wir haben die Controller der Einfachheit halber weggelassen.

In MVC dreht sich alles um Benutzeroberflächen. Der Fokus liegt auf dem Modell und der Ansicht - Definieren und Anzeigen von Daten. Beachten Sie das "Anmelde- / Benachrichtigungsprotokoll" - hier kommt Ihr Controller ins Spiel. Sie können alle gewünschten Ansichten erstellen. Solange sie sich an das Protokoll halten, müssen Sie niemals das Modell oder den Controller berühren.

Wenn Sie speziell von Web-Entwicklung sprechen, sind viele gängige Web-Frameworks mit dem Begriff MVC und seinen Komponentendefinitionen schnell und locker.

Herzog
quelle
Ich bin ein C # / Java-Entwickler (nur ein paar Projekte dort). Es scheint, dass ich falsch verstanden habe, was das Modell tut. Das Einfügen der 'Geschäftslogik' in den Controller war wirklich nur ein Nebeneffekt (mein Gedankengang ging in Ordnung, ich habe das Datenmodell (gelesen: Datenbankverbindung / Speicher), daher muss meine Geschäftslogik in den Controller eingehen, weil Ich muss es anwenden, bevor ich die Daten in der Datenbank speichere. “Alles muss nur eine Ebene tiefer vom Controller verschoben werden. Eigentlich löst dies ein Problem, das ich derzeit hatte (Unterstützung von MySQL und MSSQL in einem Programm)
Steffen Winkler,
0

Warum führen Sie keine Serviceschicht ein?

Dann wird Ihr Controller schlanker und besser lesbar sein, und Ihre gesamten Controller-Funktionen werden reine Aktionen sein.

Sie können die Geschäftslogik innerhalb der Serviceschicht beliebig zerlegen. Die Wiederverwendbarkeit von Code ist besser und es gibt keine Auswirkungen auf Modelle und Repositorys.

Anil
quelle