Javascript Modularität, serverbasierte MVC & Business Realität


32

Ich verstehe, dass dies eine sehr breite Frage ist, aber ich habe mit verschiedenen Aspekten dieses Problems individuell gearbeitet und habe Mühe, alle Konzepte und Technologien zusammenzubringen.

Ich möchte spezifizieren, dass Antworten diese Technologien einschließen sollten:

  • C #
  • MVC 3 mit Rasiermesser
  • Javascript w / jQuery

Über diese hinausgehende Informationen (wie Backbone.js , Entity Framework usw.) sind als Vorschläge willkommen, wenn sie bei der Beantwortung der folgenden Frage hilfreich sind:

Was ist unter Verwendung der oben aufgeführten Technologien eine optimale Strategie für die Organisation von Code und Logik bei gleichzeitiger Aufrechterhaltung der Skalierbarkeit und der Fähigkeit, eine reichhaltige, schnelle und saubere Benutzeroberfläche zu erstellen?

Im Idealfall sollte der Schwerpunkt auf einer Lösung liegen, die in einem Geschäfts- / Unternehmensumfeld bereitgestellt wird. In diesem Sinne wird die oben aufgeführte Technologieliste nicht geändert. Bieten Sie daher keine Lösungen mit "Sie sollten xxx anstelle von yyy verwenden , das Sie jetzt verwenden" an.

Hintergrund

Ich arbeite jeden Tag mit jQuery, habe ASP.NETs MVC übernommen und arbeite schon lange mit C #. Sie können also Lösungen vorstellen, die von fortgeschrittenen Kenntnissen dieser Technologien ausgehen.

Ich werde die Frage in kleinere Teile unterteilen , um die Beantwortung zu vereinfachen:

1. Projektstruktur

Da ich mit ASP.NET MVC (in Visual Studio 2010 ) arbeite, möchte ich eine Verzeichnisstrukturlösung, die eine gewisse Akzeptanz des Hauptlayouts für diese Art von Anwendung bietet. Sowas wie Brunch nehme ich an, aber mit ein bisschen mehr Details darüber, was jeder Ordner enthalten würde und wie es mit anderen Bereichen der App funktioniert.

2. Datenzugriff

Ich möchte meinen Datenzugriff mit einer API-ähnlichen Struktur so weit wie möglich modularisieren. Sie können viele POCO - Objekte (davon ausgehen User, UserGroup, Customer, OrderHeader, OrderDetails, usw.), aber es wird auch einige komplexe Berichte, die datenintensive SQL und sorgfältige UI - Rendering benötigen. EF + LINQ sind fantastisch für die ersteren, aber nicht so sehr für die letzteren. Ich kann nichts finden, das zu beiden Szenarien passt, ohne zu kompliziert oder zu einfach zu sein.

3. Clientseitige Code-Organisation und UI-Rendering

Wie die meisten Entwickler, die jQuery zum ersten Mal aufgegriffen haben, bin ich in die Falle geraten, Code überall dort zusammenzufügen, wo er benötigt wird. Obwohl ich seitdem große Fortschritte gemacht habe, habe ich immer noch Probleme damit, meinen Code zu modularisieren und mit verschiedenen Teilen der Benutzeroberfläche zu arbeiten, ohne den Code zu wiederholen.

Ein typisches Stück Code, das ich schreiben könnte, sieht beispielsweise so aus: Ich habe die Dinge kommentiert, die mich stören ( beachte, dass ich inzwischen auf verzögerte AJAX-Aufrufe umgestellt habe und die tatsächlichen Datenanforderungen von der DOM-Manipulation getrennt habe ):

$('#doSomethingDangerous').click(function () {
    // maybe confirm something first
    if (confirm('Are you sure you want to do this?')) {   

        // show a spinner?  something global would be preferred so I don't have to repeat this on every page 
        $('#loading').show();  

        // maybe the page should notify the user of what's going on in addition to the dialog?
        $('#results').show().html('<h2>Please wait, this may take a while...</h2>');  

        $.ajax({
            url: 'blah/DoDangerousThing',
            success: function (data) {                     
                // The results will be loaded to the DOM obviously, is there a better way to pull this type of specific code out of the data access calls?
                $('#results').empty();
                $('#results').append('<b>' + data.length + '</b> users were affected by this dangerous activity');
                $('#results').append('<ul>');

                // I've recently started to use jQuery templates for this sort of logic, is that the way to go?
                $(data).each(function (i, user) {
                    $('#results').append('<li>' + user.Username + '</li>');
                });                    
                $('#results').append('</ul>');

                // Need to hide the spinner, again would prefer to have this done elsewhere
                $('#loading').hide();
            }
        });
    }
});

Allgemeine Fragen

  • Client MVC vs. Server MVC? Mein Projekt ist bereits eine serverseitige MVC-Struktur. Gibt es also immer noch einen Bedarf an Client-MVC wie Backbone.js?
  • Sollten Javascript-Dateien für jedes Objekt (wie ein OrderHeader.js) erstellt und dann während des Builds minimiert / zusammengeführt werden? Oder sollte es nur eine geben, Order.jsdie Logik für OrderHeader, OrderDetails, Reportsetc hat?
  • Wie sollen komplexe Abfragen behandelt werden? Im Moment ist meine führende Theorie /Reports/Orders-By-Date/oder etwas in diese Richtung und ich verwende eine benutzerdefinierte SQL-Abfrage, die ein benutzerdefiniertes Dataset (oder ViewModel) in der Razor-Ansicht rendert . Aber was ist mit Paging, Sortieren usw.? Ist es besser, dies clientseitig oder serverseitig zu tun? (Angenommen, größerer Datensatz - SQL-Abfrage von 2 bis 3 Sekunden)
  • Ich habe Microsoft Project Silk durchgelesen . Ist das ein guter Weg? Wie vergleicht es sich mit Backbone.js oder anderen?
  • Ich bin sehr an eine N-Tier-Architektur gewöhnt, werfen diese Konzepte das etwas aus dem Fenster? Es scheint, dass MVC wie ein Haufen Mini-N-Tier-Abschnitte innerhalb der Front-End- oder Top-Tier-Bereiche der Vergangenheit ist.

Je genauer Ihre Antworten sind, desto besser werden sie sein. Ich habe viele hochrangige Dokumentationen und Beispiele gelesen und versuche, die Umsetzung in Beispiele aus der Praxis besser zu verstehen .


2
Sie haben viel Mühe in diese Frage gesteckt, aber es scheint mir keine Stackoverflow-Frage zu sein. Vielleicht passt der Stack-Austausch des Programmierers besser.
Pointy

3
Ich bin nicht anderer Meinung, dass es ein interessantes Thema ist, aber Stackoverflow soll sich um objektive Fragen handeln. So etwas wie diese Frage ist im Grunde das Aushängeschild für Fragen, die "wahrscheinlich Meinungen, Debatten, Argumente, Abstimmungen oder erweiterte Diskussionen erbitten".
Pointy

8
Es gibt ein Lager von Leuten, die die ganze Zeit in größtmöglichem Umfang planen, während ich ihr Geschäft stillschweigend aus der Hand nehme, weil sie zu lange mit der Planung für etwas beschäftigt waren, das nie passiert ist.
Jason Sebring

1
Ich bin mit @Pointy einverstanden, dass dies auf dem Programmierstapel gehört. Ihre Frage ist sehr interessant, und ich werde sie befolgen, weil ich immer auf der Suche nach Rat bin. Aber es ist keine objektive Frage und wird nur in Präferenzdebatten enden. Wie immer tun Sie, was für Ihre Situation am besten funktioniert ... keiner von uns weiß etwas über Ihre Netzwerkstruktur, die Anzahl der Clients oder die Verkehrsstatistik oder den Build-Prozess ... also ist die Frage viel zu vage ... alles, was ich weiß, ist Seide meiden. ;)
one.beat.consumer

1
Diese Frage entspricht genau der Definition von "zu weit gefasst" und ist daher "keine echte Frage". Wenn überhaupt, sollten die einzelnen Fragen als einzelne Fragen mit etwas Hintergrund gestellt werden (zu viel und die Leute werden es als "keine echte Frage" markieren). Beachten Sie jedoch, dass einige der von Ihnen gestellten Einzelfragen möglicherweise als "nicht konstruktiv" gekennzeichnet sind. Ich würde also vorsichtig sein, wie Sie diese Fragen stellen.
casperOne

Antworten:


10

TerryR mein Freund, du und ich sollten etwas trinken. Wir haben ähnliche Probleme.

1. Projektstruktur: Ich stimme Eduardo zu, dass die Ordnerstruktur in einer MVC-App zu wünschen übrig lässt. Sie haben Ihre Standardordner für Controller, Modelle und Ansichten. Dann wird der Ordner "Ansichten" für jeden Controller in einen anderen Ordner und einen freigegebenen Ordner unterteilt. Jeder Views / ControllerName oder Views / Shared kann in EditorTemplates und DisplayTemplates unterteilt werden. Sie können jedoch entscheiden, wie Sie Ihren Models-Ordner organisieren möchten (Sie können mit oder ohne Unterordner und zusätzliche Namespace-Deklarationen vorgehen).

Gott bewahre, dass Sie Bereiche verwenden, die die Ordnerstruktur für Controller, Modelle und Ansichten für jeden Bereich duplizieren.

/Areas
    /Area1Name
        /Controllers
            FirstController.cs
            SecondController.cs
            ThirdController.cs
        /Models
            (can organize all in here or in separate folders / namespaces)
        /Views
            /First
                /DisplayTemplates
                    WidgetAbc.cshtml <-- to be used by views in Views/First
                /EditorTemplates
                    WidgetAbc.cshtml <-- to be used by views in Views/First
                PartialViewAbc.cshtml <-- to be used by FirstController
            /Second
                PartialViewDef.cshtml <-- to be used by SecondController
            /Third
                PartialViewMno.cshtml <-- to be used by ThirdController
            /Shared
                /DisplayTemplates
                    WidgetXyz.cshtml <-- to be used by any view in Area1
                /EditorTemplates
                    WidgetXyz.cshtml <-- to be used by any view in Area1
                PartialViewXyz.cshtml <-- to be used anywhere in Area1
            _ViewStart.cshtml <-- area needs its own _ViewStart.cshtml
            Web.config <-- put custom HTML Helper namespaces in here
        Area1NameRegistration.cs <-- define routes for area1 here
    /Area2Name
        /Controllers
        /Models
        /Views
        Area2NameRegistration.cs <-- define routes for area2 here

/Controllers
    AccountController.cs
    HomeController.cs
/Models
/Views
    /Account
        /DisplayTemplates
            WidgetGhi.cshtml <-- to be used views in Views/Account
        /EditorTemplates
            WidgetGhi.cshtml <-- to be used views in Views/Account
        PartialViewGhi.cshtml <-- to be used by AccountController
    /Home
        (same pattern as Account, views & templates are controller-specific)
    /Shared
        /DisplayTemplates 
            EmailAddress.cshtml <-- to be used by any view in any area
            Time.cshtml <-- to be used by any view in any area
            Url.cshtml <-- to be used by any view in any area
        /EditorTemplates
            EmailAddress.cshtml <-- to be used by any view in any area
            Time.cshtml <-- to be used by any view in any area
            Url.cshtml <-- to be used by any view in any area
        _Layout.cshtml <-- master layout page with sections
        Error.cshtml <-- custom page to show if unhandled exception occurs
    _ViewStart.cshtml <-- won't be used automatically in an area
    Web.config <-- put custom HTML Helper namespaces in here

Das bedeutet, wenn Sie mit so etwas wie einem WidgetController arbeiten, müssen Sie in anderen Ordnern nach den zugehörigen WidgetViewModels, WidgetViews, WidgetEditorTemplates, WidgetDisplayTemplates usw. suchen diese MVC-Konventionen. Soweit ich ein Modell, einen Controller und eine Ansicht in denselben Ordner mit unterschiedlichen Namespaces lege, vermeide ich dies, da ich ReSharper verwende. Ein Namespace, der nicht mit dem Ordner übereinstimmt, in dem sich die Klasse befindet, wird schnell unterstrichen. Ich weiß, dass ich diese R # -Funktion deaktivieren kann, aber sie hilft in anderen Teilen des Projekts.

Bei Dateien, die keine Klassendateien sind, erhalten Sie von MVC standardmäßig Inhalte und Skripts. Wir versuchen, alle unsere statischen / nicht kompilierten Dateien an diesen Stellen aufzubewahren, um der Konvention zu folgen. Jedes Mal, wenn wir eine js-Bibliothek einbinden, die Themen (Bilder und / oder CSS) verwendet, werden die Themendateien irgendwo unter / content abgelegt. Bei Skripten fügen wir sie alle direkt in / scripts ein. Ursprünglich sollte dies JS Intellisense von VS erhalten, aber jetzt, da wir JS Intellisense von R # unabhängig von der Platzierung in / scripts erhalten, können wir davon abweichen und die Skripte zur besseren Organisation nach Ordnern unterteilen. Verwenden Sie ReSharper? Es ist reines Gold IMO.

Ein weiteres kleines Goldstück, das beim Refactoring sehr hilfreich ist, ist T4MVC. Auf diese Weise müssen wir keine Zeichenfolgenpfade für Bereichsnamen, Controllernamen, Aktionsnamen und sogar Dateien in Inhalten und Skripten eingeben. T4MVC tippt alle magischen Zeichenfolgen nachdrücklich für Sie. Hier ein kleines Beispiel, wie Ihre Projektstruktur nicht so wichtig ist, wenn Sie T4MVC verwenden:

// no more magic strings in route definitions
context.MapRoutes(null,
    new[] { string.Empty, "features", "features/{version}" },
    new
    {
        area = MVC.PreviewArea.Name,
        controller = MVC.PreviewArea.Features.Name,
        action = MVC.PreviewArea.Features.ActionNames.ForPreview,
        version = "december-2011-preview-1",
    },
    new { httpMethod = new HttpMethodConstraint("GET") }
);

@* T4MVC renders .min.js script versions when project is targeted for release *@
<link href="@Url.Content(Links.content.Site_css)?r=201112B" rel="stylesheet" />
<script src="@Url.Content(Links.scripts.jquery_1_7_1_js)" type="text/javascript">
</script>

@* render a route URL as if you were calling an action method directly *@
<a href="@Url.Action(MVC.MyAreaName.MyControllerName.MyActionName
    (Model.SomeId))">@Html.DisplayFor(m => m.SomeText)</a>

// call action redirects as if you were executing an action method
return RedirectToAction(MVC.Area.MyController.DoSomething(obj1.Prop, null));

2. Datenzugriff: Ich habe keine Erfahrung mit PetaPoco, bin mir aber sicher, dass es sich lohnt, dies zu überprüfen. Haben Sie bei Ihren komplexen Berichten SQL Server Reporting Services in Betracht gezogen? Oder läufst du auf einer anderen Datenbank? Entschuldigung, mir ist nicht klar, wonach genau Sie fragen. Wir verwenden EF + LINQ, setzen aber auch bestimmte Kenntnisse zum Generieren von Berichten in Domänenklassen ein. Daher haben wir das Anruf-Repository des Domänen-Service für Controller-Aufrufe, anstatt direkt über das Anruf-Repository des Controllers zu verfügen. Für Ad-hoc-Berichte verwenden wir SQL Reporting Services, was wiederum nicht perfekt ist, aber unsere Benutzer möchten, dass Daten problemlos in Excel importiert werden können, und SSRS vereinfacht dies für uns.

3. Organisation des clientseitigen Codes und Rendern der Benutzeroberfläche: Hier kann ich möglicherweise Hilfe anbieten. Nehmen Sie eine Seite aus dem Buch von MVC unauffällige Validierung und unauffällige AJAX. Bedenken Sie:

<img id="loading_spinner" src="/path/to/img" style="display:none;" />
<h2 id="loading_results" style="display:none;">
    Please wait, this may take a while...
</h2>
<div id="results">
</div>
<input id="doSomethingDangerous" class="u-std-ajax" 
    type="button" value="I'm feeling lucky" 
    data-myapp-confirm="Are you sure you want to do this?"
    data-myapp-show="loading_spinner,loading_results" 
    data-myapp-href="blah/DoDangerousThing" />

Ignorieren Sie vorerst die Ajax-Erfolgsfunktion (dazu später mehr). Sie können mit einem einzigen Skript für einige Ihrer Aktionen davonkommen:

$('.u-std-ajax').click(function () {
    // maybe confirm something first
    var clicked = this;
    var confirmMessage = $(clicked).data('myapp-confirm');
    if (confirmMessage && !confirm(confirmMessage )) { return; } 

    // show a spinner?  something global would be preferred so 
    // I dont have to repeat this on every page 
    // maybe the page should notify the user of what's going on 
    // in addition to the dialog?
    var show = $(clicked).data('myapp-show');
    if (show) {
        var i, showIds = show.split(',');
        for (i = 0; i < showIds.length; i++) {
            $('#' + showIds[i]).show();
        }
    }

    var url = $(clicked).data('myapp-href');
    if (url) {
        $.ajax({
            url: url,
            complete: function () {                     
                // Need to hide the spinner, again would prefer to 
                // have this done elsewhere
                if (show) {
                    for (i = 0; i < showIds.length; i++) {
                        $('#' + showIds[i]).hide();
                    }
                }
            }
        });
    }
});

Der obige Code kümmert sich um die Bestätigung, zeigt den Spinner, zeigt die Wartemeldung und versteckt den Spinner / die Wartemeldung, nachdem der Ajax-Aufruf abgeschlossen ist. Sie konfigurieren das Verhalten mithilfe von data- * Attributen wie den unauffälligen Bibliotheken.

Allgemeine Fragen

- Client MVC vs. Server MVC? Ich habe nicht versucht, die Aktionen, die Sie in der Erfolgsfunktion ausgeführt haben, zu librarisieren, da Ihr Controller anscheinend JSON zurückgibt. Wenn Ihre Controller JSON zurückgeben, sollten Sie sich KnockoutJS ansehen. Knockout JS Version 2.0 wurde heute veröffentlicht . Es kann direkt in Ihren JSON-Code eingefügt werden, sodass durch einen beobachtbaren Klick Daten automatisch an Ihre JavaScript-Vorlagen gebunden werden. Wenn es Ihnen andererseits nichts ausmacht, dass Ihre Ajax-Aktionsmethoden HTML anstelle von JSON zurückgeben, können sie den bereits erstellten UL mit seinen untergeordneten LI-Elementen zurückgeben, und Sie können dies mit data-myapp-response = an ein Element anhängen "Ergebnisse". Ihre Erfolgsfunktion würde dann so aussehen:

success: function(html) {
    var responseId = $(clicked).data('myapp-response');
    if (responseId) {
        $('#' + responseId).empty().html(html);
    }
}

Um meine beste Antwort darauf zusammenzufassen: Wenn Sie JSON von Ihren Aktionsmethoden zurückgeben müssen, überspringen Sie die serverseitige Ansicht. Dies ist also wirklich keine Server-MVC, sondern nur MC. Wenn Sie PartialViewResult mit HTML an Ajax-Aufrufe zurückgeben, ist dies Server-MVC. Wenn Ihre App JSON-Daten für Ajax-Aufrufe zurückgeben muss, verwenden Sie Client-MVVM wie KnockoutJS.

In beiden Fällen gefällt mir das von Ihnen gepostete JS nicht, da es Ihr Layout (HTML-Tags) mit dem Verhalten (asynchrones Laden von Daten) mischt. Wenn Sie Server-MVC mit teilweisen HTML-Ansichten oder Client-MVVM mit reinen JSON-Ansichtsmodelldaten auswählen, wird dieses Problem für Sie gelöst. Die manuelle Erstellung von DOM / HTML in JavaScript verstößt jedoch gegen die Trennung von Bedenken.

- Erstellung von Javascript-Dateien In .NET 4.5 sind anscheinend Minimierungsfunktionen enthalten . Wenn Sie den unauffälligen Weg gehen, sollte Sie nichts daran hindern, Ihre gesamte JS in 1-Skriptdatei zu laden. Ich würde vorsichtig sein, wenn Sie für jeden Entitätstyp unterschiedliche JS-Dateien erstellen würden. Am Ende wird die JS-Datei aufgelöst. Denken Sie daran, dass der Browser Ihre Skriptdatei nach dem Laden für zukünftige Anforderungen zwischenspeichern sollte.

- Komplexe Abfragen Ich halte Features wie Paginierung, Sortieren usw. nicht für komplex. Ich bevorzuge es, dies mit URLs und serverseitiger Logik zu behandeln, um die Datenbankabfragen so begrenzt wie nötig zu machen. Da wir jedoch in Azure bereitgestellt werden, ist die Abfrageoptimierung für uns wichtig. Zum Beispiel: /widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}. EF und LINQ to Entities können Seitenumbrüche und Sortierungen mit Methoden wie .Take (), .Skip (), .OrderBy () und .OrderByDescending () verarbeiten, sodass Sie während des DB-Trips das bekommen, was Sie brauchen. Ich habe noch keine Notwendigkeit für eine Client-Bibliothek gefunden, daher weiß ich ehrlich gesagt nicht viel über sie. Weitere Ratschläge hierzu finden Sie in den anderen Antworten.

- Projekt Seide Noch nie von diesem gehört, müssen Sie es überprüfen. Ich bin ein großer Fan von Steve Sanderson, seinen Büchern, seinem BeginCollectionItem HtmlHelper und seinem Blog. Allerdings habe ich keine Erfahrung mit KnockoutJS in der Produktion . Ich habe die Tutorials ausprobiert, aber ich versuche nicht, mich auf etwas festzulegen, bis es mindestens Version 2.0 ist. Wie ich bereits erwähnte, wurde KnockoutJS 2.0 gerade veröffentlicht.

- N-Tier Wenn mit Tier eine andere physische Maschine gemeint ist, dann nein, ich glaube nicht, dass irgendetwas aus einem Fenster geht. Im Allgemeinen bedeutet 3-Tier, dass Sie 3 Maschinen haben. Möglicherweise haben Sie einen Fat Client als Präsentationsebene, der auf dem Computer eines Benutzers ausgeführt wird. Der Fat Client kann auf eine Serviceebene zugreifen, die auf einem Anwendungsserver ausgeführt wird und XML oder was auch immer an den Fat Client zurückgibt. Die Serviceschicht bezieht ihre Daten möglicherweise von einem SQL-Server auf einem dritten Computer.

MVC ist eine Schicht auf einer Ebene. Ihre Controller, Modelle und Ansichten sind alle Teil Ihrer Präsentationsebene, die in der physischen Architektur eine Ebene darstellt. MVC implementiert das Model-View-Controller-Muster, in dem möglicherweise zusätzliche Ebenen angezeigt werden. Versuchen Sie jedoch, diese drei Aspekte nicht als Ebenen oder Schichten zu betrachten. Stellen Sie sich alle drei als Bedenken in Bezug auf die Präsentationsebene vor.

Update nach Pres / Bus / Datenkommentar

Okay, Sie verwenden Tier und Layer austauschbar. Normalerweise verwende ich den Begriff "Layer" für logische / Projekt- / Baugruppentrennungen und Tier für die physische Netzwerktrennung. Entschuldigung für die Verwirrung.

Es gibt im MVC-Camp einige Leute, die sagen, Sie sollten weder die "Modelle" in MVC für Ihr Entitätsdatenmodell noch Ihre Controller für die Geschäftslogik verwenden. Idealerweise sollten Ihre Modelle ansichtsspezifische ViewModels sein. Mit etwas wie Automapper nehmen Sie Ihre Entitäten aus Ihrem Domain-Modell und führen sie in ViewModels ein, die speziell für die Verwendung durch die Ansicht erstellt wurden.

Alle Geschäftsregeln sollten auch Teil Ihrer Domain sein, und Sie können sie mithilfe von Domain-Services / Factory-Patterns / was auch immer in Ihrer Domain-Schicht und nicht in der MVC-Präsentationsschicht angemessen ist implementieren. Controller sollten dumm sein, wenn auch nicht ganz so dumm wie Modelle, und der Domäne die Verantwortung für alles übertragen, was Geschäftskenntnisse erfordert. Controller verwalten den Fluss von HTTP-Anforderungen und -Antworten, aber alles, was einen echten geschäftlichen Wert hat, sollte über dem Gehaltsniveau des Controllers liegen.

Sie können also weiterhin eine mehrschichtige Architektur mit MVC als Präsentationsebene verwenden. Es ist ein Client Ihrer Anwendungs-, Dienst- oder Domänenschicht, je nachdem, wie Sie ihn erstellen. Letztendlich sollte Ihr Entitätsmodell jedoch Teil der Domäne sein, nicht Modelle in MVC.


Ich stimme dieser Antwort voll und ganz zu! Insbesondere: • Resharper ist ein MVC-Genie ... von der Fehlerprüfung bis zur IDE-Navigation - seine Nützlichkeit haut mich um! • Server-seitiges MVC ist fast immer der beste Ansatz. • MVC besteht nicht aus drei Ebenen, sondern aus einer einzelnen Präsentationsebene. Ich habe nie wirklich so darüber nachgedacht, aber es ist absolut richtig.
Scott Rippey

Sehr nette Antwort, auf jeden Fall, wonach ich auf Kosten meiner 300 Mitarbeiter gesucht habe. Die Getränke sind auf mich, wenn Sie in der Gegend von Toronto sind :)

Übrigens betrachtete ich N-Tier immer als Pres / Bus / Data, unabhängig davon, wo sie sich physisch befanden. Deshalb habe ich gesagt, dass MVC diese Architektur fast entfernt, weil es im Grunde die 3 kombiniert, was Sie sagten, stimmt etwas damit überein, gibt aber auch eine andere Perspektive darauf.

Ich würde vor dem Modell-pro-Ansicht-Ansatz von ViewModel warnen. Ich bin kürzlich auf eine Situation gestoßen, in der ich mir später gewünscht hatte, diese Abstraktion von DTO zu ViewModel nicht zu haben. Siehe: stackoverflow.com/q/7181980/109456

Im Allgemeinen sehe ich die jQuery nicht gerne und schreibe stattdessen Objekte mit Schnittstellen, die jeder serverseitige Entwickler mit JQ oder der DOM-API, die das Geschäft abwickelt, recht schnell verstehen kann. Ich mag auch das URLConfig-Konzept von Django sehr und fand es hilfreich, um Objekte für die Implementierung auf Seiten einzurichten. Ich habe keine Ahnung, was die MV? Bibliotheken sollen aber für mich tun. Sie sind nicht perfekt für das Problem geeignet. IMO und die DOM + -Ereignisdelegation sind alles, was ich für die Bearbeitung von Seiten benötige, ohne übermäßig an eine bestimmte Struktur gebunden zu sein.
Erik Reppen

6

Ich werde keine vollständige Antwort schreiben, aber einige Tipps teilen.

Meine Tipps:

1. Projektstruktur
Ich habe festgestellt, dass die Standard-MVC-Struktur für mich nicht gut ist. Ich arbeite im Allgemeinen gleichzeitig im Controller, in Ansichten und im Modell derselben Entität (denke an Produkt, Auftrag, Kunde). Ich möchte also, dass sich die Dateien im selben Ordner befinden, aber mit unterschiedlichen Namespaces.

2. Daten
Wenn Sie mit Linq-to-SQL oder EF arbeiten, werden Sie es später bereuen.
Ich benutze PetaPoco, mit dem ich SQL-Abfragen und -Updates ausführen kann, ohne dass ich dabei Mapping-Probleme habe, aber ohne neue Methoden zu erlernen und ohne die Albträume der Leistung.

Ich habe einen Codegenerator zum Erstellen der anfänglichen POCO-Klasse mit den PetaPoco-Attributen und zum Ändern der Klasse, wenn ein Feld hinzugefügt oder entfernt wird.

PetaPoco arbeitet mit dynamischen und Standardklassen, sodass Sie keine Kompromisse eingehen müssen (Massive ist alles dynamisch und Dapper alle Standardklassen).

Ich generiere auch eine Master- SQL mit dem integrierten SqlBuilder, die alle Standardverknüpfungen für die Entität enthält, aber keine WHERE-Anweisungen, sodass ich dieselbe SQL zum Abrufen einer Entität oder einer Liste wiederverwende.

3. JQuery Sie können einige Teile der Benutzeroberfläche mit einem allgemeinen jQuery-Aufruf standardisieren ( indem Sie einige Daten in das HTML-Element einfügen).

Zum Beispiel habe ich diese zum Löschen.

var deleteLinkObj;
// delete Link
$('.jbtn-borrar').click(function () {
    deleteLinkObj = $(this);  //for future use
    $('#delete-dialog').dialog('open');
    return false; // prevents the default behaviour
});
$('#delete-dialog').dialog({
    autoOpen: false, width: 400, resizable: false, modal: true, //Dialog options
    buttons: {
        "Borrar": function () {
            $.post(deleteLinkObj[0].href, function (data) {  //Post to action
                if (data == 'OK') {
                    deleteLinkObj.closest("tr").hide('fast'); //Hide Row
                }
                else {
                    alert(data);
                }
            });
            $(this).dialog("close");
        },
        "Cancelar": function () {
            $(this).dialog("close");
        }
    }
});

Ich muss nur die Klasse jbtn-borrarzu einem Hyperlink hinzufügen , und es wird ein Dialogfeld angezeigt, der Datensatz gelöscht und der ausgeblendettr

Aber überdenken Sie es nicht. Ihre App wird in jeder Ansicht mit kleinen Details glänzen.

Client MVC vs. Server MVC
Server MVC. Nutzen Sie Teilansichten, die Sie beim ersten Rendern verwenden können, und aktualisieren Sie einige Teile mit Ajax in derselben Ansicht. Siehe diesen ausgezeichneten Artikel

Wie sollen komplexe Abfragen behandelt werden (nennen wir es einen Bericht)
? Ich verwende eine Klasse mit den Berichtsparametern als Eigenschaften (praktisch für die Verwendung der MVC-Automatisierung) und eine GenerateMethode, die die Abfrage ausführt und eine Liste einer benutzerdefinierten Klasse ausfüllt (wenn Sie sie nicht verwenden) Es gibt keine Klasse, die zum ViewModel passt.)
Sie können diese Klasse als Modell der Ansicht verwenden und die Tabelle mit der generierten Liste füllen.

Microsoft Project Silk
überarchitected. Laufe so schnell du kannst in die entgegengesetzte Richtung.


Komisch, als ich Project Silk durchlas, bekam ich immer wieder dieses nervende Gefühl und konnte es nicht einordnen. Überstrukturiert hätte sein können, dass ...

3

1. Projektstruktur

Ich habe 2 Projektdateien in meiner Lösung

1) Service / Business-Schicht Ich platziere alle meine Business-Logik- und DB-Zugriffscodes und POCOs in diesem separaten Projekt. Es ist keine Datenzugriffsebene erforderlich, wenn Sie ein ORM verwenden, da das ORM die DB-Ebene bereits abstrahiert.

2) Die UI-Ebene enthält alle meine Ansichten, Controller, Modelle, Skripte und CSS

Ich versuche, dass meine Controller, Views, Skripte und CSS eine ähnliche Ordnerstruktur verwenden. Strukturieren Sie auch meine Dateien so, dass sie dem URL-Pfad so gut wie möglich entsprechen. So vermeiden Sie, dass Sie benutzerdefiniertes Routing schreiben müssen.

Verwenden Sie die DisplayTemplates, EditorTemplates, Partial Views und den Shared-Ordner so oft wie möglich.

Ich strukturiere dann alle meine Skripte so, dass sie mit den gleichen Bereichen und Controllern meiner c # -Dateien übereinstimmen. Ich hätte also eine common.js-Datei im Stammverzeichnis, eine js-Datei pro Seite und eine common.js-Datei für jeden Bereich.

CSS-Dateien Ich habe normalerweise 2 + n (wobei n die Anzahl der Bereiche ist). Die erste CSS-Datei ist nur CSS für die Zielseite, um die Ladezeit der Seite zu verkürzen (wahrscheinlich nicht so wichtig für Unternehmen). 2. CSS-Datei ist eine common.css, die alle Stile für alle anderen Seiten enthält. Dann eine weitere common.css-Datei für jeden Bereich, zum Beispiel eine AdminArea.css-Datei, die CSS für jede Admin-Seite enthält.

2. Datenzugriff

Wenn ich Entity Framework verwende, verwende ich CodeFirst, da es sehr gut mit POCOS zusammenarbeitet und Sie kein Modell pflegen müssen. nHibernate ist viel leistungsfähiger, weist jedoch eine schrittweise Lernkurve auf. Für das Paging von DB-Ergebnissen habe ich eine wiederverwendbare Util C # -Klasse und eine Patial-Ansicht, die ich für alle meine Ansichten verwende.

Für komplexe Abfragen und das Generieren von Berichten verwende ich gespeicherte Prozeduren. Sie sind viel einfacher zu schreiben und zu warten und bieten dem LINQ mehr Leistung. Sie können auch von anderen Diensten wie SSRS wiederverwendet werden. Ich verwende automapper, um das zurückgegebene Dataset in dasselbe POCOs-Entiry-Framework zu konvertieren.

3. Clientseitige Code-Organisation und UI-Rendering

Die Antwort von Eduardo Molteni hat einen guten Beispielcode. Zusätzlich würde ich definitiv die Verwendung von Knockoutjs empfehlen, da es sowohl gute Vorlagen als auch Bindungen hat. Wenn Sie JSON für alle Ihre AJAX-Aufrufe verwenden, die ich häufig verwende, ist die automatische Zuordnung der Benutzeroberfläche zu den JS-Objekten eine enorme Zeitersparnis.

Allgemeine Fragen

Komplexe Abfragen sollten in einem gespeicherten Prozess abgelegt sein. (siehe emeraldcode.com Kommentar)

Mit dieser MVC behalten Sie Ihre N-Tier-Architektur bei.


1

Ich bin kürzlich zu der Überzeugung gelangt, dass Sie, wenn Sie die drei aufgeführten Technologien verwenden möchten, zunächst von der Einführung von Orchard CMS ausgehen sollten . Ich glaube, es ist die beste Antwort auf Ihre zentrale Anforderung:

Was ist eine optimale Strategie zum Organisieren von Code und Logik bei gleichzeitiger Beibehaltung der Skalierbarkeit und der Fähigkeit, eine reichhaltige, schnelle und saubere Benutzeroberfläche zu erstellen?

Im Ochard-Szenario können Sie alles, was Sie nicht über die Konfigurationsmechanismen ansprechen können, entweder durch Hinzufügen kostenloser Online-Module oder durch Schreiben eines eigenen Moduls (natürlich C #, Rasiermesser usw.) handhaben. Code-Organisation ist eine Stärke von Orchard.

Was den Datenzugriff angeht, gibt es genug Vor- und Nachteile für einen vollständigen ORM, und ich bin der Meinung, dass ein Mikro-ORM die beste Wahl ist. Probieren Sie Massive oder Dapper . Beide wurden auf Hanselminutes vorgestellt . Ich fasse die beiden folgendermaßen zusammen: Abstraktionen von SQL brechen fast immer zusammen, wenn ein Projekt größer wird. Am Ende ist die beste Lösung für den DB-Zugriff diese Abstraktion namens SQL (ein bisschen Sarkasmus, aber wahr). Lassen Sie den Mikro-ORM damit arbeiten, und Sie haben Gold.

Wenn Sie Orchard zusammen mit den Mikro-ORMs einsetzen, können Sie Stahl wie Butter in Scheiben schneiden. Äh, das heißt, Sie können schnell Code entwickeln, skalieren und haben, der von einem Team, das die Übergabe erhält, problemlos gewartet werden kann.


0

Ich bin mir nicht sicher, wie ich diese Frage verpasst habe, aber ich werde meine zwei Cent zwei Jahre später hinzufügen.

Client MVC vs. Server MVC? Mein Projekt ist bereits eine serverseitige MVC-Struktur. Gibt es also immer noch einen Bedarf an Client-MVC wie Backbone.js?

MVC und MV? noch bevor es auf die Kundenseite gebracht wurde, hat es sich im Grunde zu einem Marketingbegriff entwickelt, der wirklich nur verspricht, dass Daten irgendwie von anderen Dingen getrennt werden, was im Großen und Ganzen eine großartige Idee ist, aber für Heimwerker nicht wirklich so schwierig ist. Ganz gleich, welchen Ansatz Sie gerade einschlagen, bevor oder mitten in der Durchführung von Änderungen an HTML, die sich auf die Präsentations- oder Interaktionsmöglichkeiten auswirken, ist der absolut schrecklichste Ort, um herauszufinden, was das Unternehmen von Ihnen mit den Daten erwartet.

An "Sichtlogik" ist nichts Besonderes. Das gleiche Prinzip sollte für alle Logik gelten. Und das ist nicht alles tun , jetzt , dass viel mehr Sinn gemacht hätte , bevor jetzt zu tun. Wenn sich alle Ihre Enten in einer Reihe befinden, bevor Sie Daten übergeben oder einen neuen Prozess einleiten, ist diese vorherige Phase wahrscheinlich für alles andere im System, das etwas Ähnliches tut, weitaus wiederverwendbarer.

Sollten für jedes Objekt Javascript-Dateien erstellt werden (z. B. eine OrderHeader.js) und dann während des Builds minimiert / zusammengeführt werden? Oder sollte es nur eine Order.js geben, die Logik für OrderHeader, OrderDetails, Reports usw. hat?

Es liegt wirklich an Ihnen, aber ich würde versuchen, mich von der Sache mit nur einer Datei und nur einer Klasse zu lösen. Ich habe nie verstanden, warum es hilfreich war, zum Beispiel die abstrakte Datei und die Schnittstelle zu finden, und die Dateien zu implementieren, usw. ... Kategorisieren Sie nach allgemeineren Bedenken. Strg + F ist nicht so schwer zu benutzen, wenn es etwas länger dauert.

Sie sollten JS jedoch niemals neu kombinieren, um Dateien im Web zu verkleinern. Browser zwischenspeichern JS, sodass Sie lediglich das Neuladen desselben JavaScript erzwingen, indem Sie alte JS in neue Dateien einfügen. Abgesehen von erstaunlichen Mengen an JavaScript ist das einzige Mal, dass Sie nicht alle JS auf der Seite haben sollten, dass eine sehr große Menge davon, die für einen Abschnitt der Site ohne Überlappung / graue Bereiche sehr spezifisch ist, auf einer bestimmten Seite niemals benötigt wird Seite.

Und FFS kümmert sich nicht um das Abhängigkeitsmanagement mit JavaScript im Web. Require.js auf Websites mit mittlerer bis geringer Komplexität bringt mich dazu, Robbenbabys zu schlagen. Stecken Sie Ihre Bibliotheken von Drittanbietern in einen oberen Block. Ihre Hausbibliotheken im zweiten Block. Und dann Ihren Implementierungscode (der niemals ein Zehntel so lang sein sollte wie Ihr interner Bibliothekscode - dh sehr prägnant und klar und leicht zu verstehen) in diesem dritten Block.

Wie sollen komplexe Abfragen behandelt werden? Im Moment lautet meine führende Theorie / Reports / Orders-By-Date / oder etwas in dieser Richtung, und ich verwende eine benutzerdefinierte SQL-Abfrage, mit der ein benutzerdefiniertes Dataset (oder ViewModel) in der Razor View dargestellt wird. Aber was ist mit Paging, Sortieren usw.? Ist es besser, dies clientseitig oder serverseitig zu tun? (angenommen, größerer Datensatz - SQL-Abfrage von 2 bis 3 Sekunden) Ich habe Microsoft Project Silk gelesen. Ist das ein guter Weg? Wie vergleicht es sich mit Backbone.js oder anderen?

Ehrlich gesagt würde ich sagen, was für Sie einfacher ist, stinkt nicht für den Kunden. Webseiten werden verdammt schnell mit moderner Technologie geladen. Wenn die Implementierung in Ajax für Sie sehr schmerzhaft ist, tun Sie dies nicht. Gehen Sie einfach mit dem, was Sie am besten wissen, und Sie können später Lust bekommen und sehen, wie es Ihnen zum Blättern gefällt. Wenn Sie eine neue komplexe App von Grund auf neu erstellen, beginnen Sie mit Essential und klicken Sie später auf OK.

Ich bin sehr an eine N-Tier-Architektur gewöhnt, werfen diese Konzepte das etwas aus dem Fenster? Es scheint, dass MVC wie ein Haufen Mini-N-Tier-Abschnitte innerhalb der Front-End- oder Top-Tier-Bereiche der Vergangenheit ist.

Es kommt wirklich darauf an, wer die Vorstellung von welchem ​​MV hat. ist. IMO, das Mikrokosmos-Ding funktioniert sehr gut. Eine Klasse von Widgets, die Daten-, Kommunikations- und ansichtsbezogene Elemente intern voneinander trennen, funktioniert hervorragend. Auf der Client-Seite ist es das Entscheidende, IMO, ein Gleichgewicht zwischen der Trennung von Bedenken aufrechtzuerhalten, ohne unnötigerweise in winzige kleine Bedenken zu zerfallen, deren Zusammenbau es schwierig macht, Dinge zu verstehen und wiederzuverwenden und zu modifizieren. Basic "duh" OOP funktioniert hier großartig. Sie wollen keine komplexen Prozesse. Sie möchten offensichtlich benannte Dinge, die bewegt und angewiesen werden können, Dinge zu tun. Hier ein paar Tipps dazu:

  • KÜSSEN Sie diese (OOP) -Schnittstelle. Ich möchte weder DOM noch jQuery oder etwas anderes sehen, was ein Server-Entwickler auf der Zwickmühle nicht schnell in meinem Implementierungscode herausfinden konnte. Alles, was diese Person wissen muss, ist, welche Klasse auf einen Div-Container geklatscht werden muss und welcher Schalter umgedreht werden muss, um eine ziemlich generische Gruppe von Benutzeroberflächen auf einer bestimmten Seite zu aktivieren. Variationen eines Themas sollten dennoch durch Übergabe gut dokumentierter / kommentierter Optionsobjekte durchgeführt werden, bevor sie sich mit document.get <anything> befassen oder etwas verstehen müssen, das über die Grundlagen von CSS hinausgeht.

  • Okay, wie machst du das? Nun, wir haben bereits ein Modell. Es heißt DOM. Und wir haben eine Event-Delegation. Wenn Sie das Sprudeln von Ereignissen nicht willkürlich unterbinden (tun Sie das nicht - es ist da, weil es nützlich ist), können Sie jeden einzelnen auch vom Körper aufheben, wenn Sie möchten. Untersuchen Sie dann die Zieleigenschaft des übergebenen Ereignisobjekts und bestimmen Sie, wer nur "was auch immer" ist. Wenn Sie ein HTML-Dokument sinnvoll strukturieren, gibt es keinen Grund, es nicht als Delegierungsmodell zu verwenden. Verhalten und Inhaltsstruktur sind selbstverständlich miteinander verknüpft. Es ist okay für die beiden, überlappende Bezeichner zu haben.

  • Zahlen Sie nicht für das Binden von Daten Und mit "Zahlen" meine ich natürlich "eine Bibliothek auf Ihre Codebasis legen, die darauf besteht, dass Sie die Dinge nur so tun - also die ganze Zeit, um einen Wundervorteil zu bekommen, der für Heimwerker eigentlich nicht schwer ist." Das Event-System von JQ macht es ziemlich einfach.

Beispiel Zeit:

function PoliticianData(){ //a constructor

    var
        that = this, //I hate 'that' but example so convention

        flavorsOfLie = {

            lies: "Oh Prism? Psh... no we're all good. There's a guy keeping an eye on that.",

            damnedLies: "50% of the people chose to not give a damn when asked whether it was better to let the terrorists win or not give a damn."

        }
    ;//end instance vars

    this.updateLies = function( lieType, newData ){
        flavorsOfLie[lieType] = newData;
        $(that).trigger({type:'update', lieType:lieType, newData: newData });
    }

    //so everytime you use the updateLies method, we can have a listener respond
    //and pass the data
}

var filthyLies = new PoliticianData();

$(filthyLies).on('update', function(e){
    stickNewDataInHTMLWithSomeFuncDefinedElsewhere(e.lieType, e.newData);
} );

filthyLies.update('damnedLies','50% of the people said they didn\'t give a damn');
//oh look, WaPo's front page just changed!
  • Nicht das Web verstecken Die Hauptquelle für Unsinn in allen frühen Versuchen, die Client-Seite für Server- und App-Entwickler einfach zu machen, hing von diesem kritischen Punkt ab. HTTP-Anfragen waren und sind nicht kompliziert. Sie benötigten keine 18! @ # $ Ing-Ebene, die den Ereignisnamen in jeder Phase der Monstrosität verwirrt, um das Verständnis zu erleichtern. Ebenso gibt es viel über den Client zu wissen, aber es gibt keinen Grund, sich vor dem HTML-Code und dem DOM zu verstecken, das mit ihm interagiert, indem ein großes Riesenmodell darauf geschlagen wird. Es ist bereits ein großes Riesenmodell und es funktioniert sehr gut. Alles, was wir brauchen, um es ein bisschen übersichtlicher zu machen, sind ein paar vernünftige OOP-Praktiken und etwas JS- und DOM-Wissen.

  • Flexibilität begünstigen

EXTjs <==== Flexibilitätsskala ====> jQuery (nicht unbedingt eines seiner Plug-Ins)

IMO, Werkzeuge, mit denen Sie schnell basteln können, sind immer die bessere Wahl. Tools, die alles für Sie erledigen, sind nur dann die richtige Wahl, wenn niemand über Ihren Kopf hinweg besonders wählerisch ist und es Ihnen nichts ausmacht, die Kontrolle über das zu übertragen, was Ihnen helfen soll. Ich habe tatsächlich Plug-Ins gesehen, die den HTML-Code validieren, um sicherzustellen, dass Sie keine anderen Elemente mit denselben genauen Anzeigeeigenschaften finden. Warum? Ich habe nur Theorien. Ich denke, es läuft darauf hinaus, dass die Vervollständiger die Idee wirklich hassen, dass jemand ihre Sachen auf eine Weise einsetzt, die nicht beabsichtigt war, und das ist immer unvermeidlich, was jemand von Ihnen in der Benutzeroberfläche verlangt.

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.