Erstens, bevor jemand betrogen schreit, fiel es mir schwer, es in einem einfachen Titel zusammenzufassen. Ein anderer Titel könnte gewesen sein "Was ist der Unterschied zwischen einem Domänenmodell und einem MVC-Modell?" oder "Was ist ein Modell?"
Konzeptionell verstehe ich unter einem Modell die Daten, die von den Ansichten und dem Controller verwendet werden. Darüber hinaus scheint es sehr unterschiedliche Meinungen darüber zu geben, was das Modell ausmacht. Was ist ein Domain-Modell im Vergleich zu einem App-Modell, einem Ansichtsmodell, einem Servicemodell usw.
In einer kürzlich gestellten Frage zum Repository-Muster wurde mir beispielsweise direkt mitgeteilt, dass das Repository Teil des Modells ist. Ich habe jedoch andere Meinungen gelesen, dass das Modell vom Persistenzmodell und der Geschäftslogikschicht getrennt werden sollte. Soll das Repository-Muster die konkrete Persistenzmethode nicht vom Modell entkoppeln? Andere Leute sagen, dass es einen Unterschied zwischen dem Domänenmodell und dem MVC-Modell gibt.
Nehmen wir ein einfaches Beispiel. Der AccountController, der im MVC-Standardprojekt enthalten ist. Ich habe mehrere Meinungen gelesen, dass der enthaltene Kontocode von schlechtem Design ist, gegen SRP verstößt usw. usw. Wenn man ein "richtiges" Mitgliedschaftsmodell für eine MVC-Anwendung entwerfen würde, welches wäre das?
Wie würden Sie die ASP.NET-Dienste (Mitgliedschaftsanbieter, Rollenanbieter usw.) vom Modell trennen? Oder würdest du überhaupt?
So wie ich es sehe, sollte das Modell "rein" sein, vielleicht mit Validierungslogik. Es sollte jedoch von den Geschäftsregeln (außer der Validierung) getrennt sein. Angenommen, Sie haben eine Geschäftsregel, nach der jemand per E-Mail benachrichtigt werden muss, wenn ein neues Konto erstellt wird. Das gehört aus meiner Sicht nicht wirklich zum Modell. Wo gehört es hin?
Möchte jemand Licht in dieses Thema bringen?
quelle
Antworten:
Die Art und Weise, wie ich es getan habe - und ich sage nicht, dass es richtig oder falsch ist - ist, meine Ansicht und dann ein Modell zu haben, das für meine Ansicht gilt. Dieses Modell hat nur das, was für meine Ansicht relevant ist - einschließlich Datenanmerkungen und Validierungsregeln. Der Controller enthält nur Logik zum Erstellen des Modells. Ich habe eine Serviceschicht, die die gesamte Geschäftslogik enthält. Meine Controller rufen meine Serviceschicht auf. Darüber hinaus ist meine Repository-Schicht.
Meine Domain-Objekte sind separat untergebracht (eigentlich in einem eigenen Projekt). Sie haben ihre eigenen Datenanmerkungen und Validierungsregeln. Mein Repository überprüft die Objekte in meiner Domäne, bevor sie in der Datenbank gespeichert werden. Da jedes Objekt in meiner Domäne von einer Basisklasse erbt, in die eine Validierung integriert ist, ist mein Repository generisch und validiert alles (und erfordert, dass es von der Basisklasse erbt).
Sie könnten denken, dass zwei Modellsätze eine Duplizierung von Code sind, und dies ist bis zu einem gewissen Grad der Fall. Es gibt jedoch durchaus vernünftige Fälle, in denen das Domänenobjekt für die Ansicht nicht geeignet ist.
Ein typisches Beispiel ist die Arbeit mit Kreditkarten. Ich muss bei der Verarbeitung einer Zahlung einen Lebenslauf verlangen, kann den Lebenslauf jedoch nicht speichern (dies ist eine Geldstrafe von 50.000 US-Dollar). Ich möchte aber auch, dass Sie Ihre Kreditkarte bearbeiten können - Änderung von Adresse, Name oder Ablaufdatum. Aber Sie werden mir beim Bearbeiten weder die Nummer noch den Lebenslauf geben, und ich werde Ihre Kreditkartennummer auf keinen Fall in Klartext auf der Seite setzen. In meiner Domain sind diese Werte zum Speichern einer neuen Kreditkarte erforderlich, da Sie sie mir geben, aber mein Bearbeitungsmodell enthält nicht einmal die Kartennummer oder den Lebenslauf.
Ein weiterer Vorteil für so viele Ebenen besteht darin, dass Sie bei korrekter Architektur eine Strukturkarte oder einen anderen IoC-Container verwenden und Teile austauschen können, ohne Ihre Anwendung zu beeinträchtigen.
Meiner Meinung nach sollte Controller-Code nur Code sein, der auf die Ansicht abzielt. Zeigen Sie dies, verbergen Sie das usw. Die Service-Schicht sollte die Geschäftslogik für Ihre App enthalten. Ich mag es, alles an einem Ort zu haben, damit es einfach ist, eine Geschäftsregel zu ändern oder zu optimieren. Die Repository-Schicht sollte relativ dumm sein - ohne Geschäftslogik und nur Ihre Daten abfragen und Ihre Domänenobjekte zurückgeben. Durch die Trennung der Ansichtsmodelle vom Domänenmodell haben Sie viel mehr Flexibilität bei benutzerdefinierten Validierungsregeln. Dies bedeutet auch, dass Sie nicht alle Daten in ausgeblendeten Feldern in Ihre Ansicht kopieren und zwischen Client und Server hin und her verschieben müssen (oder sie im Backend neu erstellen müssen).
Während alles ausgebreitet und überlagert zu sein scheint, hat es den Zweck, auf diese Weise aufgebaut zu werden. Ist es perfekt nicht wirklich. Aber ich ziehe es einigen früheren Entwürfen vor, Repositorys vom Controller aufzurufen und Geschäftslogik in Controller, Repository und Modell zu mischen.
quelle
Ich habe mich zu oft gefragt, wie genau die MVC-Elemente in eine herkömmliche Webanwendungsstruktur passen, in der Sie Ansichten (Seiten), Controller, Dienste und Datenobjekte (Modell) haben. Wie Sie sagten, gibt es viele Versionen davon.
Ich glaube, die Verwirrung besteht aufgrund der oben genannten, weithin akzeptierten Architektur, die das (angebliche) -anti-Muster des "anämischen Domänenmodells" verwendet. Ich werde nicht auf viele Details zur "Anti-Patternness" des anämischen Datenmodells eingehen (Sie können sich meine Bemühungen ansehen, um die Dinge hier zu erklären (Java-basiert, aber für jede Sprache relevant)). Kurz gesagt bedeutet dies, dass unser Modell nur Daten enthält und die Geschäftslogik in Services / Managern platziert wird.
Nehmen wir jedoch an, wir haben eine domänengesteuerte Architektur und unsere Domänenobjekte sind so, wie sie erwartet werden - sie haben sowohl Status- als auch Geschäftslogik. Und in dieser domänengetriebenen Perspektive kommen die Dinge zusammen:
Ich denke, das beantwortet Ihre Hauptfragen. Die Dinge werden kompliziert, wenn wir weitere Ebenen hinzufügen, wie z. B. die Repository-Ebene. Es wird häufig vorgeschlagen, dass es von der im Modell platzierten Geschäftslogik aufgerufen wird (und daher hat jedes Domänenobjekt einen Verweis auf ein Repository). In meinem Artikel, den ich verlinkt habe, argumentiere ich, dass dies keine bewährte Methode ist. Und dass es in der Tat keine schlechte Sache ist, eine Serviceschicht zu haben. Übrigens schließt domänengesteuertes Design die Serviceschicht nicht aus, sondern soll "dünn" sein und nur Domänenobjekte koordinieren (also keine Geschäftslogik).
Für das Paradigma des anämischen Datenmodells, das weit verbreitet ist (zum Guten oder zum Schlechten), wäre das Modell sowohl die Serviceschicht als auch Ihre Datenobjekte.
quelle
Meiner Meinung nach,
Modell -
Sollte keine Geschäftslogik enthalten, sollte sie steckbar sein (WCF-ähnliches Szenario). Es wird zum Binden an die Ansicht verwendet, daher sollte es Eigenschaften haben.
Geschäftslogik -
Es sollte auf "Domain Services Layer" platziert werden, es ist insgesamt eine separate Ebene. Außerdem wird hier eine weitere Ebene "Anwendungsdienste" hinzugefügt.
App Services kommuniziert mit der Domain Services-Schicht, um die Geschäftslogik anzuwenden und zuletzt das Modell zurückzugeben.
Der Controller wird also den Anwendungsdienst nach dem Modell fragen und der Ablauf wird wie folgt ablaufen:
quelle
Das MVC-Muster und das Asp.net-Framework unterscheiden nicht, wie das Modell aussehen soll.
Zu den eigenen Beispielen von MS gehören Persistenzklassen im Modell. Ihre Frage zur Mitgliedschaft im Modell. Das hängt davon ab. Gehören Klassen in Ihrem Modell etwas? Gibt es einen Zusammenhang zwischen der Anmeldung und den angezeigten Daten? Gibt es eine Filterung des Datenteils eines Berechtigungssystems, das bearbeitet werden kann? Hat wer zuletzt einen Objektteil Ihrer Domain aktualisiert oder bearbeitet, wie es jemand anderes sehen muss, oder etwas für die Backend-Unterstützung?
Das E-Mail-Beispiel hängt auch davon ab. Kennen Sie sich mit Domain-Eventing oder insbesondere mit Eventing aus? Haben Sie einen separaten Dienst zum Versenden von E-Mails? Ist das Senden einer E-Mail Teil Ihrer Domain oder handelt es sich um ein Problem auf Anwendungsebene außerhalb des Bereichs Ihres Systems? Muss die Benutzeroberfläche wissen, ob eine E-Mail erfolgreich gesendet wurde oder nicht? Müssen E-Mails, die nicht gesendet werden können, erneut versucht werden? Muss der Inhalt der gesendeten E-Mail für Support- oder Kundendienstanforderungen gespeichert werden?
Diese Art von Fragen ist zu weit gefasst und subjektiv, aber ich beantworte sie, damit Sie und jeder, der Sie gewählt hat, dies verstehen können.
Ihre Anforderungen / Zeitpläne / Ressourcen fließen alle in die Architektur Ihres Systems ein. Auch das Einnahmemodell kann sich auswirken. Sie müssen auch das Muster berücksichtigen, nach dem Sie fotografieren. DDD unterscheidet sich stark von Persistence-as-Model-Anwendungen, und alle dazwischen liegenden Slops gelten auch für bestimmte Apps. Schießen Sie zum Testen der App? All dies wirkt sich aus.
quelle