.NET MVC-Projektarchitektur / Layering

11

Wie implementieren Sie bei der Planung der Architektur für eine mittelgroße MVC-Webanwendung die Ebenen, die so entkoppelt wie möglich und einfach zu testen sind? Nehmen wir an, ich verwende zuerst Code als Datenzugriff.

Ich habe Probleme damit, was "Geschäftslogik" zu definieren ist und wie es mit der Datenschicht interagieren soll. Wäre Geschäftslogik am Beispiel einer Fahrzeugverkaufsanwendung Klassen, die Aufgaben wie die Berechnung des Steuerbandes für bestimmte Fahrzeuge, den Vergleich von Meilen pro Gallone-Statistiken usw. ausführten? Die Geschäftseinheiten (z. B. Autos, Lieferwagen, Motorräder) würde ich zusammen mit meiner DataContextKlasse in die Datenschicht einfügen .

Auch was würde Anwendungslogik im Gegensatz zu Geschäft ausmachen - ich vermute Dinge wie Validierungen von Sitzungen / Benutzereingaben?

So kann beispielsweise ein Autocontroller ein Aktions- / Ansichtsergebnis zurückgeben, in dem die zehn besten Autos aufgelistet sind, die nach Typ und bester mpg gefiltert sind. ICarRepositoryNehmen wir also an, ich habe ein 'carRepo' in meinen Controller injiziert (unter Verwendung des Repository-Musters / DI). Ich filtere meine Autos aus einem Aktionsmethodenparameter, zvar cars = carRepo.getCarsByType("hatchback");

Daher habe ich das Datenzugriffswissen mithilfe eines Repositorys von meinem Controller ferngehalten, um die Geschäftslogik mithilfe eines Domänenmodells vom Controller fernzuhalten - var result = new MpgCalculator (cars); - Angenommen, ich benötige die Rechnerklasse, da sie zusätzliche Logik ausführen muss, um die beste Kraftstoffeffizienz zu berechnen, und nicht nur Entitäten aus der Datenbank laden / filtern. Jetzt habe ich einen Datensatz für meine Ansicht zum Rendern, der ein Repository zum Abrufen von der Datenzugriffsschicht und ein domänenspezifisches Objekt zum Verarbeiten und Ausführen geschäftsbezogener Aufgaben für diese Daten verwendet.

Mache ich hier Fehler? Müssen wir noch das Repository-Muster verwenden oder kann ich einfach gegen eine Schnittstelle codieren, um das ORM zu entkoppeln und zu testen? Sollten sich zu diesem Thema die Schnittstellendefinitionen in der Domänen- / Geschäftsschicht befinden, da sich meine konkreten Datenzugriffsklassen dbcontext in der Datenschicht befinden, was bedeutet, dass meine anderen Schichten nicht betroffen sind, wenn die Datenzugriffstechnologie jemals geändert wird?

Nach dem, was ich bisher studiert habe, sieht meine Struktur folgendermaßen aus:

MVC Internet Application -> Die Standard-Internetprojektmodelle hier sind ViewModels

Domänen- / Geschäftsschicht -> Geschäftsspezifische Klassen / Modelle, mit denen Controller Domänenentitäten aus der Datenschicht verarbeiten können, bevor sie an die relevanten Ansichten weitergeleitet werden

Repository-Abstraktion notwendig? -> Ich höre viele Debatten darüber, besonders wenn ich ein ORM benutze

Datenschicht -> Entitätsklassen (Auto, Van, Motorrad), DbContext - Schicht der konkreten Datenzugriffstechnologie

Michael Harper
quelle

Antworten:

26

Sie haben viele bewegliche Teile in Ihrer Frage, die viele Konzepte berühren, aber hier sind meine grundlegenden Ratschläge, wenn es darum geht, über eine mittelgroße bis große MVC-Anwendung nachzudenken:

Präsentation <---> Business Logic <---> Datenzugriff

Erstens ist es am besten nicht als „eine MVC - Anwendung“ des App zu denken. Es ist eine Anwendung, die das MVC-Muster als Präsentationskomponente verwendet. Wenn Sie auf diese Weise darüber nachdenken, können Sie Ihre geschäftlichen Logikprobleme von Ihren Präsentationsproblemen trennen . Vielleicht ist es für kleine Anwendungen in Ordnung, alles auf den Datenbankzugriff in die MVC-Struktur zu stapeln, aber für eine mittelgroße bis große Anwendung wird es schnell unhaltbar.

MVC (Präsentation)

In Ihrer App sollte sich die ASP.NET MVC-Komponente mit der Transformation von Geschäftsdaten für Anzeigezwecke (Modelle), der Anzeige der Benutzeroberfläche (Ansichten) und Kommunikationsproblemen wie Routing, Authentifizierung, Autorisierung, Anforderungsvalidierung, Antwortbehandlung und dem befassen wie (Controller). Wenn Sie Code haben, der etwas anderes tut, gehört er nicht in die MVC-Komponente .

Repository / ORM (Datenzugriff)

Auch in Ihrer App sollte sich die Datenzugriffsschicht mit dem Abrufen und Speichern persistenter Daten befassen. In der Regel handelt es sich dabei um eine relationale Datenbank, aber es gibt viele andere Möglichkeiten, wie Daten beibehalten werden können. Wenn Sie Code haben, der keine persistenten Daten liest oder speichert, gehört er nicht in die Datenschicht . Ich habe meine Gedanken zur ORM / Repository-Diskussion zuvor über SO geteilt, aber um es noch einmal zusammenzufassen, ich halte ein ORM aus mehreren Gründen nicht für dasselbe wie ein Repository.

Geschäftslogik

Jetzt haben Sie also Ihre Präsentationsschicht (MVC) und Ihre Datenschicht (Repository oder ORM) ... Alles andere ist Ihre Geschäftslogikschicht (BLL). Ihr gesamter Code, der entscheidet, welche Daten abgerufen werden sollen, komplizierte Berechnungen durchführt oder Geschäftsentscheidungen trifft, sollte hier sein. Normalerweise organisiere ich meine Geschäftslogik in Form von "Services", auf die meine Präsentationsschicht zurückgreifen kann, um die angeforderte Arbeit zu erledigen. Alle meine Domain-Modelle existieren hier.

Ihr Ansatz

Hier bricht Ihr Ansatz für mich ein wenig zusammen. Sie beschreiben Ihren MVC-Controller als den Ort, an dem Sie Daten aus dem Repository abrufen und den MPGCalculator auffordern, einige Arbeiten usw. auszuführen. Ich würde meinen Controller dies nicht tun lassen, sondern all dies an einen Dienst delegieren in der BLL.

Mit anderen Worten, ich würde kein Repository und keinen MPGCalculator in den Controller einfügen, da dies dem Controller zu viel Verantwortung überträgt (er behandelt bereits alle oben erwähnten Controller- Dinge). Stattdessen würde ich all das von einem Dienst in der BLL erledigen lassen und die Ergebnisse an den Controller zurückgeben. Der Controller kann dann die Ergebnisse in das richtige Modell umwandeln und an die richtige Ansicht weitergeben. Der Controller enthält keine Geschäftslogik, und die einzigen Dinge, die in den Controller injiziert werden, sind die entsprechenden BLL-Dienste.

Wenn Sie dies auf diese Weise tun, ist Ihre Geschäftslogik (z. B. bei einer Reihe von Fahrzeugen, Berechnung des MPG und Sortierung vom Besten zum Schlechtesten ) unabhängig von Präsentations- und Persistenzproblemen. Es befindet sich normalerweise in einer Bibliothek, die weder die Datenpersistenzstrategie noch die Präsentationsstrategie kennt oder sich darum kümmert.

Eric King
quelle
Hallo Eric, ausgezeichnete Antwort - in Bezug auf die Repositories gehe ich davon aus, dass die konkreten Klassen in der Datenzugriffsschicht und das 'ICarRepository' usw. in der Geschäfts- / Service-Schicht leben würden? Dann könnte ich meinem Controller Dienste hinzufügen, die je nach Anforderung ein oder mehrere Repositorys enthalten können.
Michael Harper
@MichaelHarper Ja, das klingt nach einer vollkommen guten Vorgehensweise.
Eric King
1
Während die Authentifizierung ein Controller-Problem darstellt (verschiedene Benutzeroberflächen authentifizieren sich unterschiedlich), würde ich sagen, dass die Autorisierung Geschäftslogik ist und zur Geschäftsschicht gehört. Sind Sie einverstanden?
Tom
1
@tom Ja, du hast einen guten Punkt. Ich dachte an eine einfache Autorisierung, da der Benutzer Zugriff auf diese Route hat , aber es kann noch viel mehr sein. Der Teil "viel mehr dazu" gehört in die Geschäftsschicht.
Eric King
1
@HunterNelson Wenn Sie einem Ansichtsmodell zuordnen, sollte die Zuordnung dort erfolgen, wo sich die Ansicht in der Präsentationsebene befindet. Es würde nirgendwo anders Sinn machen.
Eric King
0

Es sieht so aus, als ob alles für Ihre Struktur korrekt ist. Das einzige, bei dem ich mir nicht sicher bin, ist, dass Sie erwähnen, dass die Modelle in MVC "ViewModels" sind und dass Ihre Controller mit der Domänenschicht kommunizieren. Ich denke, dies ist sinnvoll, wenn Ihr Standardmuster darin besteht, den Controller für den Zugriff auf die Domänenschicht zu verwenden und dann Ihre "ViewModels" als ansichtsspezifischere Zusammenstellungen von Informationen aus mehreren Domänenentitäten zu verwenden, wie dies für diese bestimmte Ansicht sinnvoll ist. Wenn Sie das tun, sind Sie wahrscheinlich in Ordnung.

Es gibt eine Meinung, dass Sie eine vollständige Abstraktion Ihrer Domänenschicht in Ihrer MVC-Anwendung haben sollten, wenn Sie welche haben werden. Persönlich verursacht mir der Gedanke, dies in einer Unternehmensanwendung zu tun, starke mentale Schmerzen.

Ich bevorzuge die Verwendung des Repository-Musters, um den Zugriff auf die Datenschicht zu verwalten, da dies die Testbarkeit und Flexibilität verbessert. Die beiden Dinge, die dazu neigen, die drastischsten Änderungen vorzunehmen, sind die Benutzeroberfläche und die Datenbank. Stellen Sie sich vor, einige der Informationen, die Sie direkt aus der Datenbank abrufen, werden so geändert, dass sie nicht aus einem Datenbankaufruf, sondern aus einem Serviceabruf abgerufen werden müssen, oder einige Informationen werden in eine andere Datenbank verschoben, für die eine andere .edmx erforderlich ist Datei. Das Repository-Muster bietet eine Abstraktion, um dies zu unterstützen.

wpenberthy
quelle
Vielen Dank für die Antwort. William 😊 Ich würde meine Geschäftsobjekte / Logik- und Domänenentitäten als 'Modelle' betrachten, die der Controller verwendet, um Benutzeraktionen und Ansichtsmodelle als Ansichtsspezifische Modelle zu verarbeiten, die Gruppen von Modellen usw. enthalten könnten.
Michael Harper