Fat Model / Thin Controller vs. Service Layer [geschlossen]

83

Ich entwickle seit vielen Jahren Unternehmensanwendungen mit .Net. Meine Apps verfügen normalerweise über ein Domänenmodell, das Entitäten enthält, die SQL DB-Tabellen zugeordnet sind. Ich verwende ein Repository-Muster, eine Abhängigkeitsinjektion und eine Serviceschicht.

Vor kurzem haben wir begonnen, an MVC 3-Projekten zu arbeiten, und wir hatten eine Debatte darüber, wo welche Logik platziert werden soll. Ich bin auf eine dünne Controller / FAT-Modellarchitektur gestoßen und habe mich gefragt, wie die Service-Schicht dazu passen würde

Option 1 - Modellgespräche mit Diensten

Controller ist dünn, ruft Methoden für die Modelle auf. Die Modelle "wissen", wie sie sich selbst aus der Datenbank laden und mit Repositorys oder Diensten kommunizieren können. Zum Beispiel hat customerModel eine Load (id) -Methode und lädt den Kunden und einige untergeordnete Objekte wie GetContracts ().

Option 2 - Controller spricht mit Diensten

Der Controller fordert die Dienste auf, Modellobjekte abzurufen. Die Logik des Ladens / Speicherns usw. befindet sich in der Serviceschicht. Das Modell ist ein reines Entitätsmodell mit nur Daten.

Warum sollte Option 1 eine bessere Wahl sein, insbesondere wenn wir über Unternehmensanwendungen sprechen? Meine Erfahrung zeigt mir, dass ich Bedenken trennen, Modelle UND Controller so dünn wie möglich halten und spezialisierte Services haben muss, die die Geschäftslogik ausführen (imkl. Die DB-Interaktion).

Vielen Dank für alle Ratschläge und Hinweise auf gute Ressourcen.

PeterFromCologne
quelle

Antworten:

94

All dies hängt von der Absicht und den Anforderungen Ihrer Bewerbung ab.

Das heißt, hier ist mein Vorschlag für "mittelgroße" Webanwendungen (kein lokales Restaurant und keine Twitter / Facebook).

  1. Lean Domain Modeling

    Trockene Objekte im POCO-Stil, die die MVC-Architektur Ihrer Webanwendung vorzugsweise nicht kennen, um so lose wie möglich von Ihrer speziellen Implementierung gekoppelt zu bleiben. Möglicherweise kann die Klassenbibliothek sogar für die Verwendung in einer externen Anwendung umgepackt werden, z. B. eine REST-API über einen WCF-Webdienst ).

    "Modell" in MVC bedeutet am genauesten das Modell, das dem Controller bekannt ist, und damit das Modell, das für die Ansicht vorgesehen ist .

    In kleineren (häufig Tutorial-) Anwendungen sind die Entitätsmodelle Ihrer "Anwendungs- / Domänenmodellschicht" häufig dieselben instanziierten Objekte, die der Controller an eine Ansicht sendet.

    In größeren Anwendungen verwenden Entwickler häufig die Grundsätze der MVVM-Architektur und beginnen mit der Verwendung separater View Model-Objekte. Die Controller rufen häufig Dienste der mittleren Ebene auf, die mit den unten aufgeführten unsichtbaren Entitäten zusammenarbeiten. In diesem Szenario bedeutet das M in MVC am genauesten das Ansichtsmodell.

  2. Robuste Serviceschicht

    Dies bedeutet keine fettleibige Logik, sondern gut geschriebene Einzweckdienste. Das Codieren Ihrer Geschäftslogik in Diensten außerhalb des Modells ist zwar etwas "prozeduraler" als das reine "OOP", hilft jedoch bei der losen Kopplung, beim Testen und bei der flexiblen Bereitstellung (z. B. n-Tier-Bereitstellung) erheblich.

    In meiner persönlichen Praxis codiere ich Dienste sowohl auf der Datenebene, die ich als meine Verhaltensmodellierung der POCO-Objekte (Persistenzmechanik, Validierung auf niedriger Ebene usw.) betrachte, als auch übergeordnete Dienste (Geschäfts- / Workflow-Funktion) näher die MVC-Mechanik.

  3. Lean Controller

    Ich stelle sicher, dass mein Controller lediglich der Trainer ist , da es weder das Spiel (Dienste) noch der Spieler (Entitätsmodell oder Ansichtsmodell) ist, sondern einfach entscheidet, wer welche Position spielt und welches Spiel zu machen ist. Meine Controller machen zwei Dinge:

    1. Rufen Sie Dienste auf, die mit den Entitäts- / Domänenmodellen interagieren

    2. Bereiten Sie ein Ansichtsmodell für die entsprechende Ansicht vor.

    Sogar authentifizierte / autorisierte Controller-Aktionen werden über injizierte Dienste / Attribute ausgeführt.


EDIT 1:

Beachten Sie, dass dies nicht bedeutet, dass Ihr Entitäts- / Domänenmodell anämisch ist oder sein muss. ORMs, Repositories und Fabriken, Validierung oder Zustandsmechanik sind willkommen. Dies bedeutet nur für Anwendungen mit mittlerem Maßstab, dass das Modell in MVC das Modell darstellt, das für den Controller bestimmt ist, um es an Ihre Ansicht weiterzugeben .

Hoffentlich wird dieser Punkt Fowler-Apostel beruhigen, die glauben, dass das anämische Datenmodell ein Anti-Muster ist . Zur gleichen Zeit, es ist eine etwas Verfahrenswinkel als OOP reflektieren , wo es rein umfassen Verhalten in den modellierten Klassen ist.

Es gibt keine "ultimative Wahrheit", aber mit diesem Muster können Sie Ihre Anwendungen einfach erstellen, testen und bereitstellen - und dabei viel Wiederverwendbarkeit und Skalierbarkeit beibehalten.


EDIT 2:

Das heißt, selbst für Anwendungen mit bescheidener Größe ist es viel zu üblich, ein System über das Architekturen (das ein Wort Nerds erfunden hat?) Zu verfügen. Zum Beispiel das Umschließen eines ORM mit einem Repository-Muster und das anschließende Schreiben von Diensten zur Verwendung des Repositorys ... all dies ist gut für die Trennung von Bedenken und dergleichen, aber wenn Ihr Projekt dies nicht erfordert (und es nicht sehr wahrscheinlich bald erfordert) ) solche Dinge, baue es nicht. Es ist nichts Falsches daran, das Repository insgesamt zu überspringen, Thin Business Services (z. B. Abfrageklassen) für einen ORM zu schreiben oder sogar Ihren Controller direkt mit ihm sprechen zu lassen. Es hängt alles vom Maßstab ab.


EDIT 3:

Ich wollte darauf hinweisen, dass diese Erklärung und dieser Rat für den Kontext einer serverseitigen MVC-Architektur wie ASP.Net gelten, nicht für Clentseiten-Frameworks wie Knockout oder Backbone.

one.beat.consumer
quelle
11
Dies ist fast genau das gleiche Entwurfsmuster, das ich verwende, außer dass der Controller keine Kenntnisse über das Repository hat. Der Controller interagiert nur mit Diensten, die wiederum mit Repositorys interagieren.
Lester
2
@Lester Ich habe bearbeitet, um das zu klären. In 95% der Fälle ist dies auch nicht der Fall. Die Idee ist, dass die Dienste dies tun. Auf kleinen Apps kann es übertrieben sein, aber es ist eine gute Praxis für jeden und viel einfacher mit einem IoC-Container zu warten
one.beat.consumer
1
+1 @ one.beat.consumer: Dies ist der gleiche Ansatz, den ich bei meinen Projekten verfolge. Manchmal führt eine zu puristische Einhaltung der Regeln zu überkomplizierten Lösungen, und Sie können erleben, dass Sie mehr Vorteile aus einer in der Praxis bewährten Lösung ziehen können folgt nicht perfekt GOF-Mustern
themarcuz
7
@ivowiblo- Modell in MVC ist das Datenmodell, das Ihr Controller vorbereitet und an die Ansicht übergibt. Aus diesem Grund kann es sein, dass Ihr 'Anwendungsmodell' (Domänenmodell, Modellschicht, was auch immer Sie beschriften) die MVC-Bibliotheken überhaupt nicht kennt und sogar außerhalb Ihrer Lösung auf einem separat verteilten System vorhanden ist. In MVC wird eine Anforderung einfach an die Steuerung weitergeleitet. Der Controller stellt ein Ansichtsmodell zusammen (Daten für die Präsentationsschicht). Wenn dieses Modell dasselbe instanziierte Objekt ist, das Sie in Ihrer Persistenzmechanik verwendet haben, ist dies möglicherweise eine schlechte Übung, aber es ist zulässig, was bedeutet, dass es keine ausschließliche Definition gibt.
one.beat.consumer
2
+1 für Beachten Sie, dass "Modell" in MVC am genauesten das Modell bedeutet, das dem Controller bekannt ist, und somit das Modell, das für die Ansicht vorgesehen ist.
Luiz Damim
16

Sie müssen mehr über MVC wissen, bevor wir besprechen, wo alles abgelegt werden soll. Nun, wenn Sie dem Muster folgen möchten. Andernfalls können Sie jetzt aufhören zu lesen.

Das Muster ist sehr locker definiert. Es gibt nichts, was besagt, wie der Controller, die Ansicht oder das Modell aussehen oder wie sie strukturiert sein sollten. Das Muster besagt einfach, dass Sie die Teile trennen sollten und wie sie miteinander interagieren sollen. Schauen wir uns also etwas mehr darüber an, was sie sind (meine Interpretation).

MVC

Modell Das Modell kann alles sein. Dies kann ein Webservice, Ihre Repositorys, Ihre Serviceklassen oder einfach Ihre Domain-Modelle sein. Das Modell ist alles, was verwendet wird, um die Informationen zu erhalten, die Sie benötigen. Betrachten Sie das "Modell" als Ebene und nicht nur als einzelnes Objekt.

Controller Der Controller ist ein Kleber. Es nimmt die Informationen aus dem Modell und passt sie an die Ansicht an und umgekehrt.

Ansicht Die Ansicht sollte nur das rendern, was der Benutzer sieht.

Beachten Sie, dass Sie das Modell nicht mit Ansichtsmodellen verwechseln sollten. Microsoft hätte den Ordner "Model" eigentlich "ViewModels" nennen sollen, da dies der Fall ist. Ich würde Informationen aus dem "Modell" nicht direkt in den Ansichten verwenden. Wenn Sie dies nicht tun, müssen Sie das Modell ändern, wenn die Ansicht geändert wird, und umgekehrt.

Die Antwort

Das Modell ist kein Ansichtsmodell, sondern eine Ebene. Alles im Modell wird verwendet, um die für die Ansicht erforderlichen Informationen abzurufen. Der Controller nimmt diese Informationen und fügt sie in ein einzelnes Ansichtsmodell ein.

Eine einzelne Controller-Aktion kann einen oder mehrere Aufrufe des "Modells" verwenden, um die von der Ansicht benötigten Informationen zusammenzustellen.

Das bedeutet, dass Ihre zweite Option am besten geeignet ist, wenn Sie eine Anwendung erhalten möchten, die einfach zu warten und zu erweitern ist.

Beachten Sie, dass möglicherweise keine Serviceschicht erforderlich ist. Sie können den OR / M direkt von den Controllern aus aufrufen. Wenn Sie jedoch feststellen, dass Sie Code duplizieren oder Fat Controller erhalten, verschieben Sie die Logik einfach auf eine Serviceebene. Nichts als der Controller ist von dieser Änderung betroffen, da Sie geeignete Ansichtsmodelle verwenden.

jgauffin
quelle
3
Ich wünschte, ASP.NET MVC hätte stattdessen den Namen ASP.NET ModelView View Controller erhalten. Es wäre ein schrecklicher Name, aber zumindest würde er seine wahre Bedeutung vermitteln :)
Hector Correa
Selbst nach der Verwendung von ASP.NET MVC habe ich eine Weile gebraucht, um zu erkennen, dass Modell nicht Ansichtsmodell bedeutet.
Lester
@ one.beat.consumer: Mein Punkt über das Modell war, dass es alles sein kann. Es ist nur eine Schicht da draußen. Erstellen Sie es so, wie es für die Anwendung geeignet ist. Ich habe es so ausgedrückt, da so viele denken, dass das Modell in ASP.NET MVC das Ansichtsmodell ist oder dass die VM und das Modell dasselbe sind.
Jgauffin
Ich denke, dass ich die Frage anspreche. Meine Interpretation von, customerModelwie er in der Frage spricht, ist ein Ansichtsmodell. Wenn er versteht, dass es nicht so ist, ist die Antwort offensichtlicher.
Jgauffin
2
@ jgauffin-Semantik ist hier wichtig - in MVC impliziert "Modell" keine "Modellebene"; Es wird nur ein Modellobjekt impliziert, das für den Controller geeignet ist, um an eine Ansicht übergeben zu werden . In umfangreichen Anwendungen kennt die MVC-Architektur häufig nicht einmal die Modell- / Datenschicht oder wie auch immer Sie sie nennen. Meine bearbeitete Antwort versucht, diese Verwirrung zu erklären. Hauptsächlich wenn Apps klein sind, ist die zusätzliche Trennung von Modell- und Ansichtsmodell häufig nicht erforderlich. Daher neigen die Benutzer dazu, ihre Modelle zu markieren und die Controller ein Repository usw. verwenden zu lassen Apps in voller Größe, dies sollte selten passieren.
one.beat.consumer
0

Option 1: Sie könnten denken, dass model == service. Modell ist auch die Business-Schicht.

Option 2 ist ein Anti-Pattern für ein anämisches Domänenmodell. http://en.wikipedia.org/wiki/Anemic_domain_model

Imre L.
quelle
Denken Sie daran, dass das Nennen von etwas als Anti-Muster mehr Kontext benötigt! Viele Anwendungen benötigen kein Domänenmodell, da sie hauptsächlich CRUD-Operationen ausführen.
Rookian
Das Domänenmodell besteht nur aus Daten mit "Metadaten". Wenn Sie keine Metadaten haben, ist dies in Ordnung. Ich habe das Wort "Anti-Pattern" entfernt, weil Sie in diesem Punkt Recht haben. Ich mag die akzeptierte Antwort wirklich und meine eigene hätte stattdessen ein Kommentar sein sollen.
Imre L
0

Option 2 wird als Fat Stupid Ugly Controllers-Architektur beschrieben ( Verweis auf den Autor dieses Ausdrucks ). Diese Lösung ist im Allgemeinen gegen den MVC-Geist, da sie die Trennung von Bedenken aufhebt.

Sergey Kudriavtsev
quelle
1
public ActionResult FetchApple() { return View(_groceryService.GetApple("Granny Smith")); }ist ziemlich schlank, wenn du mich fragst.
one.beat.consumer
4
Meine Lektüre dieses FSUC-Artikels stimmt nicht mit Option 2 oben überein. Das Beispiel, das der FSUC-Autor bereitstellt, zeigt nicht die Verwendung einer Serviceschicht, in der die gesamte Bestelllogik gekapselt ist. Stattdessen wird angezeigt, dass der Controller mit Geschäftslogik geladen wurde. Und die Wiederverwendbarkeit der Geschäftslogik aufgrund eines Controllers geht jetzt verloren.
Marvo