Modułowość JavaScript, MVC oparte na serwerze i rzeczywistość biznesowa


32

Rozumiem, że to bardzo szerokie pytanie, ale pracowałem z różnymi aspektami tego problemu indywidualnie i staram się połączyć wszystkie koncepcje i technologie razem.

Chciałbym sprecyzować, że odpowiedzi powinny obejmować następujące technologie:

  • DO#
  • MVC 3 z brzytwą
  • JavaScript w / jQuery

Wszystko, co wykracza poza te (np. Backbone.js , Entity Framework itp.), Jest mile widziane jako sugestie, jeśli pomagają odpowiedzieć na pytanie:

Przy użyciu wyżej wymienionych technologii, jaka jest optymalna strategia organizacji kodu i logiki przy jednoczesnym zachowaniu skalowalności i możliwości stworzenia bogatego, szybkiego, czystego interfejsu użytkownika?

Najlepiej byłoby położyć nacisk na rozwiązanie wdrażane w środowisku biznesowym / korporacyjnym. W tej notatce powyższa lista technologii nie zostanie zmieniona, więc nie oferuj rozwiązań z „powinieneś używać xxx zamiast yyy , którego używasz teraz”.

tło

Codziennie pracuję z jQuery, adoptowałem MVC ASP.NET i od dłuższego czasu pracuję z C #. Możesz więc przedstawić rozwiązania zakładające średnio-zaawansowaną wiedzę na temat tych technologii.

Zorganizuję pytanie w mniejsze części aby ułatwić odpowiedź na:

1. Struktura projektu

Biorąc pod uwagę, że pracuję z ASP.NET MVC (w Visual Studio 2010 ), chciałbym rozwiązania struktury katalogów, które oferują pewną akceptację głównego układu tego typu aplikacji. Coś jak Brunch przypuszczam, ale z nieco bardziej szczegółowo o tym, co każdy folder będzie zawierać i jak to działa z innymi obszarami aplikacji.

2. Dostęp do danych

Chciałbym zmodularyzować mój dostęp do danych tak bardzo, jak to możliwe, ze strukturą typu API. Można założyć, wiele obiektów POCO ( User, UserGroup, Customer, OrderHeader, OrderDetailsitp), ale nie będzie też kilka złożonych raportów, które wymagają intensywnego danych SQL i ostrożny renderowania UI. EF + LINQ są fantastyczne dla tych pierwszych, ale nie tak bardzo dla tych drugich. Nie mogę znaleźć czegoś, co wydaje się pasować do obu scenariuszy, nie będąc zbyt skomplikowanym ani zbyt prostym.

3. Organizacja kodu po stronie klienta i renderowanie interfejsu użytkownika

Jak większość programistów, którzy po raz pierwszy wybrali jQuery, wpadłem w pułapkę łączenia kodu wszędzie tam, gdzie trzeba było go znaleźć, ale szybko odkryłem, że gromadzi się i robi się brzydki. Mimo że od tamtej pory przeszedłem skoki i ograniczenia, nadal mam problem z modularyzacją mojego kodu i pracą z różnymi częściami interfejsu bez powtarzania kodu.

Na przykład typowy fragment kodu, który mógłbym napisać, powinien wyglądać tak: skomentowałem rzeczy, które mnie niepokoją ( zauważam, że od tego czasu zmieniłem na używanie odroczonych wywołań AJAX i oddzieliłem rzeczywiste żądania danych od manipulacji DOM ):

$('#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();
            }
        });
    }
});

Ogólne pytania

  • Klient MVC kontra serwer MVC? Mój projekt jest już strukturą MVC po stronie serwera, więc czy nadal istnieje potrzeba klienta MVC takiego jak Backbone.js?
  • Czy pliki Javascript powinny być tworzone dla każdego obiektu (jak an OrderHeader.js), a następnie minimalizowane / scalane podczas kompilacji? A może powinna istnieć Order.jslogika dla OrderHeader, OrderDetails, Reportsetc?
  • Jak należy obsługiwać złożone zapytania? Obecnie moją wiodącą teorią jest /Reports/Orders-By-Date/coś podobnego i używam niestandardowego zapytania SQL, które renderuje niestandardowy zestaw danych (lub ViewModel) w widoku Razor. Ale co ze stronicowaniem, sortowaniem itp.? Czy lepiej to zrobić po stronie klienta czy serwera? (przy założeniu większego zestawu danych - 2 do 3 sekund zapytania SQL)
  • Przeczytałem Microsoft Project Silk . Czy to dobra droga? Jak to się ma do Backbone.js lub innych?
  • Jestem bardzo przyzwyczajony do architektury wielopoziomowej, czy te koncepcje wyrzucają to z okna? Wygląda na to, że MVC jest jak garść mini-warstwowych sekcji w ramach tego, co w przeszłości byłoby front-endem lub najwyższym poziomem.

Ponownie, im bardziej szczegółowe będą twoje odpowiedzi, tym lepiej będą. Przeczytałem dużo dokumentacji i przykładów na wysokim poziomie , staram się lepiej zrozumieć, tłumacząc ją na przykłady z prawdziwego świata .


2
Włożyłeś wiele wysiłku w to pytanie, ale nie wydaje mi się to pytaniem Stackoverflow. Być może programista wymiana stosów byłaby lepszym rozwiązaniem.
Pointy

3
Nie zgadzam się, że jest to interesujący temat, ale Stackoverflow powinien dotyczyć obiektywnych pytań. Coś w rodzaju tego pytania jest w zasadzie dzieckiem z plakatu, w którym „można uzyskać opinie, debaty, argumenty, ankiety lub dłuższe dyskusje”.
Pointy

8
Jest obóz ludzi, którzy cały czas planują największą skalę, podczas gdy ja cicho zabieram ich interesy, ponieważ zbyt długo planowali coś, co nigdy się nie wydarzyło.
Jason Sebring,

1
Zgadzam się z @Pointy, że należy to do stosu programistów. Twoje pytanie jest bardzo interesujące i będę na nie odpowiadać, ponieważ zawsze szukam porady. Nie jest to jednak pytanie obiektywne i kończy się tylko na preferencyjnych debatach. Jak zawsze, rób to, co najlepiej pasuje do Twojej sytuacji ... nikt z nas nie wie nic o strukturze sieci, liczbie klientów lub statystykach ruchu, ani o procesie kompilacji ... więc pytanie brzmi WOLNO zbyt niejasne ... wszystko co wiem to unikaj Jedwabiu. ;)
one.beat.consumer

1
To pytanie pasuje do samej definicji „zbyt szerokiego” i dlatego „nie jest to prawdziwe pytanie”. Jeśli już, to poszczególne pytania powinny być zadawane jako indywidualne pytania z niewielkim tłem (zbyt wiele, a ludzie oznaczą to jako „nie prawdziwe pytanie”). Bądź jednak ostrożny, ponieważ niektóre z zadawanych przez ciebie indywidualnych pytań prawdopodobnie zostałyby oznaczone jako „niekonstruktywne”, więc uważam, jak je zadajesz.
casperOne

Odpowiedzi:


10

TerryR mój przyjacielu, ty i ja powinniśmy się napić. Mamy podobne problemy.

1. Struktura projektu: Zgadzam się z Eduardo, że struktura folderów w aplikacji MVC pozostawia coś do życzenia. Masz swoje standardowe foldery Kontrolery, Modele i Widoki. Ale potem folder Widoki zostaje podzielony na inny folder dla każdego kontrolera oraz folder współdzielony. I każde Widoki / Nazwa Kontrolera lub Widoki / Udostępnione można podzielić na EditorTemplates i DisplayTemplates. Pozwala jednak zdecydować, jak zorganizować folder Modele (możesz to zrobić z podfolderami lub bez nich i dodatkowymi deklaracjami przestrzeni nazw).

Boże, zabraniaj, że używasz Obszarów, które duplikują strukturę folderów Kontrolery, Modele i Widoki dla każdego obszaru.

/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

Oznacza to, że jeśli pracujesz z czymś takim jak WidgetController, musisz zajrzeć do innych folderów, aby znaleźć powiązane WidgetViewModels, WidgetViews, WidgetEditorTemplates, WidgetDisplayTemplates itp. Choć jest to uciążliwe, trzymam się tego i nie odchodzę od te konwencje MVC. Jeśli chodzi o umieszczanie modelu, kontrolera i widoku w tym samym folderze, ale z różnymi przestrzeniami nazw, unikam tego, ponieważ używam ReSharper. Spowoduje to podkreślenie przestrzeni nazw, która nie pasuje do folderu, w którym znajduje się klasa. Wiem, że mógłbym wyłączyć tę funkcję R #, ale pomaga to w innych częściach projektu.

W przypadku plików nieklasowych MVC zapewnia Treść i Skrypty od razu po wyjęciu z pudełka. Staramy się zachować wszystkie nasze pliki statyczne / nieskompilowane w tych miejscach, ponownie, zgodnie z konwencją. Za każdym razem, gdy dołączamy bibliotekę js, która używa motywów (obrazów i lub css), wszystkie pliki motywów przechodzą gdzieś w / content. W przypadku skryptu po prostu umieszczamy je wszystkie bezpośrednio w / scripts. Pierwotnie miało to na celu uzyskanie inteligencji JS z VS, ale teraz, gdy otrzymujemy inteligencję JS z R # niezależnie od umieszczenia w / scripts, przypuszczam, że moglibyśmy od tego odstąpić i podzielić skrypty według folderów, aby lepiej zorganizować. Czy używasz ReSharper? To jest czyste złoto IMO.

Kolejnym małym kawałkiem złota, który bardzo pomaga przy refaktoryzacji, jest T4MVC. Korzystając z tego, nie musimy wpisywać ścieżek ciągów dla nazw obszarów, nazw kontrolerów, nazw akcji, a nawet plików w treści i skryptach. T4MVC zdecydowanie wpisuje dla ciebie wszystkie magiczne ciągi. Oto mała próbka tego, jak struktura projektu nie ma większego znaczenia, jeśli używasz T4MVC:

// 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. Dostęp do danych: Nie mam doświadczenia z PetaPoco, ale jestem pewien, że warto to sprawdzić. Czy w przypadku złożonych raportów rozważałeś usługi raportowania SQL Server? A może używasz innej bazy danych? Przepraszam, nie jestem pewien, o co dokładnie prosisz. Korzystamy z EF + LINQ, ale mamy też pewną wiedzę na temat generowania raportów w klasach domen. Mamy więc repozytorium wywołań usług domenowych kontrolerów zamiast bezpośredniego repozytorium wywołań kontrolerów. W przypadku raportów ad hoc korzystamy z usług SQL Reporting Services, co znowu nie jest idealne, ale nasi użytkownicy lubią mieć możliwość łatwego wprowadzania danych do Excela, a SSRS ułatwia nam to.

3. Organizacja kodu po stronie klienta i renderowanie interfejsu użytkownika: Myślę, że w tym miejscu mogę zaoferować pomoc. Weź stronę z książki MVC dyskretnej weryfikacji i dyskretnej AJAX. Rozważ to:

<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" />

Na razie zignoruj ​​funkcję sukcesu ajax (więcej na ten temat później). Możesz uciec od jednego skryptu dla niektórych swoich działań:

$('.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();
                    }
                }
            }
        });
    }
});

Powyższy kod zajmie się potwierdzeniem, wyświetleniem pokrętła, wyświetleniem komunikatu oczekiwania i ukryciem komunikatu pokrętła / oczekiwania po zakończeniu wywołania ajax. Zachowania konfiguruje się za pomocą atrybutów data- *, takich jak dyskretne biblioteki.

Ogólne pytania

- Klient MVC kontra serwer MVC? Nie próbowałem zsyntetyzować działań podjętych w funkcji sukcesu, ponieważ wygląda na to, że kontroler zwraca JSON. Jeśli kontrolery zwracają JSON, warto przyjrzeć się KnockoutJS. Knockout JS wersja 2.0 została wydana dzisiaj . Można podłączyć bezpośrednio do JSON, dzięki czemu możliwe do zaobserwowania kliknięcie może automatycznie powiązać dane z szablonami javascript. Z drugiej strony, jeśli nie masz nic przeciwko temu, że twoje metody akcji ajax zwracają HTML zamiast JSON, mogą zwrócić już skonstruowane UL z jego potomkami LI, i możesz dołączyć to do elementu za pomocą data-myapp-response = „wyniki”. Twoja funkcja sukcesu wyglądałaby wtedy następująco:

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

Podsumowując moją najlepszą odpowiedź na to pytanie, jeśli musisz zwrócić JSON z metod działania, pomijasz widok po stronie serwera, więc to naprawdę nie jest serwer MVC - to tylko MC. Jeśli zwrócisz PartialViewResult z html do wywołań ajax, to jest to serwer MVC. Jeśli więc aplikacja musi zwrócić dane JSON dla wywołań ajax, użyj klienta MVVM, takiego jak KnockoutJS.

Tak czy inaczej, nie podoba mi się opublikowany przez Ciebie JS, ponieważ łączy on Twój układ (tagi HTML) z zachowaniem (asynchroniczne ładowanie danych). Wybór MVC serwera z częściowymi widokami HTML lub klienta MVVM z czystymi danymi JSON viewmodel rozwiąże ten problem, ale ręczne tworzenie DOM / HTML w javascript narusza separację problemów.

- Tworzenie plików JavaScript Najwyraźniej funkcje minimalizacji są dostępne w .NET 4.5 . Jeśli pójdziesz dyskretną drogą, nic nie powinno Cię powstrzymywać przed załadowaniem całego JS w 1 pliku skryptu. Byłbym ostrożny przy tworzeniu różnych plików JS dla każdego typu encji, skończy się to eksplozją pliku JS. Pamiętaj, że po załadowaniu pliku skryptu przeglądarka powinna zapisać go w pamięci podręcznej na przyszłe żądania.

- Złożone zapytania Nie uważam, że takie funkcje jak paginacja, sortowanie itp. Są złożone. Wolę sobie z tym poradzić za pomocą logiki adresów URL i logiki po stronie serwera, aby zapytania db były tak ograniczone, jak to konieczne. Jesteśmy jednak wdrożeni na platformie Azure, dlatego optymalizacja zapytań jest dla nas ważna. Na przykład: /widgets/show-{pageSize}-per-page/page-{pageNumber}/sort-by-{sortColumn}-{sortDirection}/{keyword}. EF i LINQ do encji mogą obsługiwać paginację i sortowanie za pomocą metod takich jak .Take (), .Skip (), .OrderBy () i .OrderByDescending (), dzięki czemu otrzymujesz to, czego potrzebujesz podczas podróży db. Nie znalazłem jeszcze potrzeby posiadania klienta, więc szczerze mówiąc, niewiele o nich wiem. Więcej odpowiedzi na ten temat znajdziesz w innych odpowiedziach.

- Projekt jedwabiu Nigdy o nim nie słyszałem, trzeba będzie to sprawdzić. Jestem wielkim fanem Steve'a Sandersona, jego książek, jego BeginCollectionItem HtmlHelper i jego bloga. To powiedziawszy, nie mam żadnego doświadczenia z KnockoutJS w produkcji . Sprawdziłem jego tutoriale, ale staram się nie angażować w coś, dopóki nie będzie to co najmniej wersja 2.0. Jak wspomniałem, KnockoutJS 2.0 został właśnie wydany.

- N-poziom Jeśli przez poziom masz na myśli inną maszynę fizyczną, to nie, nie sądzę, żeby coś wychodziło z okna. Ogólnie 3-poziom oznacza, że ​​masz 3 maszyny. Więc możesz mieć grubego klienta jako warstwę prezentacji, która działa na komputerze użytkownika. Gruby klient może uzyskać dostęp do warstwy usługi, która działa na serwerze aplikacji i zwraca XML lub cokolwiek do grubego klienta. Warstwa usług może pobierać dane z serwera SQL na 3. komputerze.

MVC to jedna warstwa, na 1 poziomie. Kontrolery, modele i widoki są częścią Warstwy prezentacji, która jest 1 poziomem w architekturze fizycznej. MVC implementuje wzorzec Model-View-Controller, gdzie możesz zobaczyć dodatkowe warstwy. Staraj się jednak nie myśleć o tych 3 aspektach jako o poziomach lub warstwach. Spróbuj pomyśleć o wszystkich 3 z nich jako o problemach z warstwą prezentacji.

Aktualizacja po komentarzu pres / bus / data

Okej, więc używasz warstw i warstw zamiennie. Zazwyczaj używam terminu „warstwa” dla logicznych / projektowych / asemblacyjnych podziałów, a poziomu dla fizycznego oddzielenia sieci. Przepraszam za zamieszanie.

W obozie MVC znajduje się sporo osób, które mówią, że nie należy używać „modeli” w MVC dla modelu danych jednostki ani używać kontrolerów do logiki biznesowej. Idealnie byłoby, gdyby twoje modele były modelami ViewModels. Używając czegoś takiego jak Automapper, bierzesz swoje byty z modelu domeny i dodajesz je do ViewModels, specjalnie zaprojektowanych do użycia przez widok.

Wszelkie reguły biznesowe powinny również stanowić część domeny i można je wdrożyć za pomocą usług domenowych / wzorca fabrycznego / cokolwiek odpowiedniego w warstwie domeny, a nie w warstwie prezentacji MVC. Administratorzy powinni być głupi, choć nie tak głupi jak modele, i powinni ponosić odpowiedzialność za domenę za wszystko, co wymaga wiedzy biznesowej. Kontrolery zarządzają przepływem żądań i odpowiedzi HTTP, ale wszystko, co ma prawdziwą wartość biznesową, powinno znajdować się powyżej poziomu wynagrodzenia kontrolera.

Tak więc nadal możesz mieć architekturę warstwową, z MVC jako warstwą prezentacji. Jest to klient warstwy aplikacji, warstwy usługi lub warstwy domeny, w zależności od tego, w jaki sposób ją tworzysz. Ale ostatecznie twój model bytu powinien być częścią domeny, a nie modeli w MVC.


Całkowicie zgadzam się z tą odpowiedzią! W szczególności: • Resharper jest geniuszem MVC ... od sprawdzania błędów do nawigacji IDE, jego użyteczność mnie zaskakuje! • MVC po stronie serwera jest prawie zawsze najlepszym podejściem • MVC to nie 3 osobne warstwy, to pojedyncza warstwa prezentacji - nigdy tak naprawdę o tym nie myślałem, ale jest absolutnie poprawny.
Scott Rippey,

Bardzo fajna odpowiedź, zdecydowanie tego szukałem kosztem mojego 300 powtórzeń. Napoje są na mnie, jeśli jesteś w okolicy Toronto :)

btw Zawsze uważałem N-tier za Pres / Bus / Data, niezależnie od tego, gdzie fizycznie siedzieli. Właśnie dlatego powiedziałem, że MVC prawie usuwa tę architekturę, ponieważ w zasadzie łączy 3, to, co powiedziałeś, nieco się z tym zgadza, ale daje też inne spojrzenie na to.

Ostrzegam przed podejściem ViewModel, model na widok. Niedawno wpadłem na sytuację, w której później żałowałem, że nie mam tej abstrakcji od DTO do ViewModel. Zobacz: stackoverflow.com/q/7181980/109456

Zasadniczo nie lubię widzieć jQuery i zamiast tego piszę obiekty z interfejsami, które każdy deweloper po stronie serwera byłby w stanie zrozumieć dość szybko dzięki JQ lub interfejsowi API DOM wykonującym działalność wewnątrz. Bardzo podoba mi się również koncepcja URLConfig firmy Django i uważam ją za pomocną przy konfigurowaniu obiektów do implementacji na stronach. Nie mam pojęcia co MV? biblioteki mają dla mnie zrobić. Nie nadają się idealnie do rozwiązania problemu, IMO i delegowanie zdarzeń DOM + to wszystko, czego potrzebuję do obsługi stron bez nadmiernego powiązania z określoną strukturą.
Erik Reppen

6

Nie zamierzam pisać pełnej odpowiedzi, ale chcę podzielić się wskazówkami.

Moje wskazówki:

1. Struktura projektu
Odkryłem, że domyślna struktura MVC nie jest dla mnie dobra. Ogólnie pracuję w kontrolerze, widokach i modelu tego samego podmiotu (myśl produkt, zamówienie, klient) jednocześnie. Tak więc lubię mieć pliki w tym samym folderze, ale z różnymi przestrzeniami nazw.

2. Dane
Jeśli zdecydujesz się na Linq-to-SQL lub EF, pożałujesz później.
Korzystam z PetaPoco, który pozwala mi uruchamiać pobieranie i aktualizowanie rekordów SQL bez bólu związanego z mapowaniem, ale bez uczenia się nowego sposobu wykonywania zadań i bez koszmarów związanych z wydajnością.

Mam generator kodu do tworzenia początkowej klasy POCO z atrybutami PetaPoco, a następnie zmieniam klasę, gdy jakieś pole zostanie dodane lub usunięte.

PetaPoco działa z klasami dynamicznymi i standardowymi, więc nie masz żadnych kompromisów (Massive jest cały dynamiczny, a Dapper wszystkie standardowe klasy)

Generuję również główny SQL, używając wbudowanego SqlBuilder, który zawiera wszystkie standardowe sprzężenia dla encji, ale nie ma GDZIE, więc ponownie używam tego samego SQLa do pobierania jednej encji lub listy.

3. Jquery Możesz ustandaryzować niektóre części interfejsu użytkownika za pomocą ogólnego wywołania jQuery (upychanie niektórych danych wewnątrz elementu HTML).

Na przykład mam to do usunięcia.

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");
        }
    }
});

Muszę tylko dodać klasę jbtn-borrardo hiperłącza, aby wyświetlić okno dialogowe, usunąć rekord i ukryćtr

Ale nie przemyśl tego. Twoja aplikacja będzie świecić za pomocą drobnych akcentów w każdym widoku.

Klient MVC kontra serwer MVC
Serwer MVC. Skorzystaj z widoków częściowych, których możesz użyć podczas wstępnego renderowania, i odśwież niektóre części za pomocą Ajaxu, używając tego samego widoku. Zobacz ten doskonały artykuł

Jak należy obsługiwać złożone zapytania (nazwijmy to Raportem)
Używam klasy, która ma parametry Raportu jako właściwości (przydatne przy korzystaniu z automatycznego mapowania MVC) oraz Generatemetody, która wykonuje zapytanie i wypełnia Listę klasy niestandardowej (jeśli nie nie ma klasy pasującej do ViewModel)
Możesz użyć tej klasy jako modelu widoku i wypełnić tabelę wygenerowaną listą.

Projekt Microsoftu Silk
Overarchitected. Biegnij jak najszybciej w przeciwnym kierunku.


Zabawne, kiedy czytałem w Project Silk, miałem dokuczliwe uczucie i nie mogłem tego umiejscowić. Być może nadarchitektem mogło być ...

3

1. Struktura projektu

Mam 2 pliki projektu w swoim rozwiązaniu

1) Warstwa serwisowa / biznesowa Wszystkie moje logiki biznesowe i kod dostępu DB oraz POCO umieszczam w tym osobnym projekcie. Warstwa dostępu do danych nie jest potrzebna, jeśli używasz ORM, ponieważ ORM już abstrahuje warstwę DB.

2) Warstwa interfejsu użytkownika zawiera wszystkie moje widoki, kontrolery, modele, skrypty, CSS

Staram się, aby moje kontrolery, widoki, skrypty i CSS korzystały z podobnej struktury folderów. Skonstruuj również moje pliki tak, aby jak najlepiej pasowały do ​​ścieżki URL. Aby uniknąć konieczności pisania niestandardowego routingu.

Używaj DisplayTemplates, EditorTemplates, widoków częściowych i folderu współdzielonego w jak największym stopniu.

Następnie tworzę wszystkie moje skrypty, aby pasowały do ​​tych samych obszarów, kontrolerów moich plików c #. Miałem więc plik common.js w katalogu głównym, plik js na stronę i plik common.js dla każdego obszaru.

Pliki CSS Zwykle mam 2 + n (gdzie n to liczba obszarów) 1. plik CSS to CSS tylko dla strony docelowej, aby pomóc w szybszym ładowaniu strony (prawdopodobnie nie tak ważne dla środowiska biznesowego / korporacyjnego) 2. plik CSS jest common.css, który ma wszystkie style dla wszystkich innych stron. Następnie inny plik common.css dla każdego obszaru, na przykład plik AdminArea.css, który zawiera CSS dla każdej strony administratora.

2. Dostęp do danych

Jeśli używam Entity Framework, korzystam z CodeFirst, ponieważ działa on bardzo dobrze z POCOS i nie masz modelu do utrzymania. nHibernate jest znacznie potężniejszy, ale ma bardziej zaawansowaną krzywą uczenia się. Do stronicowania wyników DB mam do ponownego użycia klasę c # i widok patialny, którego używam do wszystkich moich widoków.

Do złożonych zapytań i generowania raportów używam procedur przechowywanych. Są znacznie łatwiejsze do pisania i konserwacji oraz oferują większą moc LINQ. Mogą być również ponownie wykorzystane przez inne usługi, takie jak SSRS. Korzystam z automappera, aby przekonwertować zestaw danych zwróconych z powrotem na te same ramy frameworku POCO.

3. Organizacja kodu po stronie klienta i renderowanie interfejsu użytkownika

Odpowiedź Eduardo Molteni ma dobry przykładowy kod. Dodatkowo zdecydowanie polecam użycie knockoutjs, ponieważ ma on zarówno dobre szablony, jak i wiązania. Jeśli używasz JSON do wszystkich wywołań AJAX, których często używam, posiadanie automatycznej mapy interfejsu użytkownika do obiektów JS to ogromna oszczędność czasu.

Ogólne pytania

Złożone zapytania powinny znajdować się w przechowywanym proc. (patrz komentarz emeraldcode.com)

Nadal zachowujesz swoją architekturę wielopoziomową za pomocą tego MVC.


1

Ostatnio przekonałem się, że jeśli planujesz korzystać z trzech wymienionych technologii, powinieneś zacząć od przyjęcia CMS Orchard . Uważam, że jest to najlepsza pojedyncza odpowiedź na twoje główne wymagania:

Jaka jest optymalna strategia organizacji kodu i logiki przy jednoczesnym zachowaniu skalowalności i możliwości stworzenia bogatego, szybkiego i czystego interfejsu użytkownika?

W scenariuszu Ochard wszystko, czego nie można rozwiązać za pomocą mechanizmów konfiguracyjnych, można by wówczas obsłużyć poprzez dodanie bezpłatnych modułów online lub napisanie własnego modułu (którym oczywiście są C #, maszynka do golenia itp.). Organizacja kodu to siła Orcharda.

Jeśli chodzi o dostęp do danych, istnieje wystarczająco dużo zalet i wad pełnej ORM, że doszedłem również do wniosku, że najlepszym rozwiązaniem jest mikro-ORM. Spróbuj Massive lub Dapper . Oba były prezentowane w Hanselminutes . Podsumuję te dwie rzeczy, mówiąc: abstrakcje z SQL prawie niezmiennie psują się w miarę skalowania projektu. Ostatecznie najlepszym rozwiązaniem dla dostępu do bazy danych jest ta abstrakcja zwana SQL (nieco sarkazmu, ale prawda). Pozwól mikro-ORM z tym pracować, a ty masz złoto.

Połącz sad z mikro-ORM, a możesz pokroić stal jak masło. Eee, co oznacza, że ​​możesz szybko opracowywać, skalować i mieć kod, który jest łatwy do utrzymania przez zespół, który odbiera przekazanie.


0

Nie jestem pewien, jak przeoczyłem to pytanie, ale dodam dwa centy dwa lata później.

Klient MVC vs. serwer MVC? Mój projekt jest już strukturą MVC po stronie serwera, więc czy nadal istnieje potrzeba klienta MVC takiego jak Backbone.js?

MVC i MV? nawet zanim został przeniesiony na stronę klienta, zasadniczo przekształcił się w termin marketingowy, który naprawdę tylko obiecuje, że w jakiś sposób dane zostaną oddzielone od innych rzeczy, co jest w zasadzie świetnym pomysłem, ale nie jest tak trudne dla majsterkowiczów. Bez względu na to, jakie podejście podejmiesz, tuż przed lub w trakcie wprowadzania zmian w HTML, które wpływają na możliwości prezentacji lub interakcji, jest to absolutnie najbardziej okropne miejsce do ustalenia, co firma chce zrobić z danymi.

Nie ma nic specjalnego w „logice widoku”. Ta sama zasada powinna mieć zastosowanie do całej logiki. Oznacza to, że nie róbcie teraz niczego, co byłoby o wiele bardziej rozsądne. Kiedy wszystkie twoje kaczki są w rzędzie przed przekazaniem danych lub zainicjowaniem nowego procesu, poprzednia faza prawdopodobnie będzie znacznie bardziej przydatna dla czegokolwiek innego w systemie, który robi coś podobnego.

Czy pliki Javascript powinny zostać utworzone dla każdego obiektu (jak OrderHeader.js), a następnie zminimalizowane / scalone podczas kompilacji? A może powinien istnieć Order.js, który ma logikę dla OrderHeader, OrderDetails, raportów itp.?

To naprawdę zależy od ciebie, ale starałbym się uciec od jednego pliku, jednej klasy. Nigdy nie zrozumiałem, dlaczego pomocne było na przykład znalezienie pliku abstrakcyjnego i interfejsu oraz implementacja plików itp. Kategoryzuj na podstawie szerszych problemów. ctrl + f nie jest trudny w użyciu, jeśli trwa trochę dłużej.

To powiedziawszy, nigdy nie powinieneś rekombinować JS, aby zmniejszyć pliki w sieci. Przeglądarki buforują JS, więc wymuszasz ponowne ładowanie tego samego JavaScript, umieszczając stary JS w nowych plikach. Z wyjątkiem olbrzymiej ilości skryptów JavaScript, jedyną sytuacją, w której nie powinieneś mieć całego JS na stronie, jest to, że bardzo duża jego ilość, która jest bardzo specyficzna dla jednej sekcji witryny bez nakładających się / szarych obszarów, nigdy nie będzie potrzebna w danym strona.

A FFS nie przeszkadza w zarządzaniu zależnościami za pomocą JavaScript w Internecie. Require.js na stronach o średniej i niskiej złożoności sprawia, że ​​chcę klubować foki dla dzieci. Umieść biblioteki stron trzecich w górnym bloku. Twoje biblioteki wewnętrzne w drugim bloku. A następnie kod implementacyjny (który nigdy nie powinien być dziesiąty tak długo, jak kod biblioteki wewnętrznej - tj. Bardzo zwięzły, przejrzysty i łatwy do zrozumienia) w tej trzeciej kodzie.

Jak należy obsługiwać złożone zapytania? Obecnie moją wiodącą teorią jest / Reports / Orders-By-Date / lub coś podobnego do tego i używam niestandardowego zapytania SQL, które renderuje niestandardowy zestaw danych (lub ViewModel) w widoku Razor. Ale co ze stronicowaniem, sortowaniem itp.? Czy lepiej to zrobić po stronie klienta czy serwera? (przy założeniu większego zestawu danych - 2 do 3 sekund zapytania SQL) Przeczytałem Microsoft Project Silk. Czy to dobra droga? Jak to się ma do Backbone.js lub innych?

Szczerze mówiąc, powiedziałbym, co jest dla ciebie łatwiejsze, co nie śmierdzi dla klienta. Strony internetowe ładują się cholernie szybko na nowoczesnych technologiach. Jeśli implementacja w Ajax jest dla ciebie bardzo uciążliwa, nie rób tego. Po prostu skorzystaj z tego, co wiesz najlepiej, a potem możesz się później spodobać i zobaczyć, jak ci się podoba przywoływanie. Jeśli budujesz nową, złożoną aplikację od zera, zacznij od niezbędnego i uderz w porządek później.

Jestem bardzo przyzwyczajony do architektury wielopoziomowej, czy te koncepcje wyrzucają to z okna? Wygląda na to, że MVC jest jak garść mini-warstwowych sekcji w ramach tego, co w przeszłości byłoby front-endem lub najwyższym poziomem.

To naprawdę zależy od tego, kto ma fantazję na temat tego, co MV? jest. IMO, mikrokosmos zwykle działa bardzo dobrze. Klasa widgetu, który wewnętrznie oddziela dane, komunikację i rzeczy związane z widokiem, działa świetnie. W sieci klienckiej najważniejszą rzeczą, IMO, jest utrzymanie równowagi między utrzymywaniem problemów oddzielnie, bez niepotrzebnego rozpadania się na małe drobne problemy, których ponowny montaż utrudnia zrozumienie, ponowne użycie i modyfikację. Podstawowe OOP „duh” działa tutaj świetnie. Nie chcesz skomplikowanych procesów. Chcesz oczywiście nazwać rzeczy, które można przenosić i kazać im robić różne rzeczy. Oto kilka wskazówek na temat tego frontu:

  • KISS, ten interfejs (OOP) Nie chcę widzieć DOM, jQuery ani niczego innego, kto uderzyłby w szczyptę po stronie serwera, nie mógł dość szybko zrozumieć mojego kodu implementacyjnego. Wszystko, co ta osoba powinna wiedzieć, to jaką klasę uderzyć w kontener div i jaki przełączyć na flip, aby uaktywnić dość ogólny zestaw interfejsów użytkownika na danej stronie. Wariacje na temat powinny być nadal realizowane przez przekazywanie dobrze udokumentowanych / skomentowanych obiektów opcji, zanim zaczną one patrzeć na document.get <wszystko> lub zrozumieć cokolwiek poza najzwyklejszymi podstawami CSS.

  • Ok, więc jak to robisz? Mamy już model. To się nazywa DOM. I mamy delegację wydarzeń. Jeśli nie wyłączasz masowo bulgotania zdarzeń (nie rób tego - jest tam, ponieważ jest to przydatne), możesz odebrać każdy nawet z ciała, jeśli chcesz. Następnie sprawdź właściwość docelową przekazanego obiektu zdarzenia i określ, kto „wielbił”. Jeśli rozsądnie konstruujesz dokument HTML, nie ma powodu, aby nie używać go jako modelu delegowania. Zachowanie i struktura treści są naturalnie powiązane. Jest w porządku, że oba mają nakładające się identyfikatory.

  • Nie płać za wiązanie danych. Przez „płacę” rozumiem oczywiście „przykleić bibliotekę do bazy kodu, która nalega, abyś robił różne rzeczy, więc cały czas, aby uzyskać jakąś cudowną korzyść, która tak naprawdę nie jest trudna do samodzielnego wykonania”. System zdarzeń JQ sprawia, że ​​jest to całkiem łatwe.

Przykładowy czas:

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!
  • Nie ukrywaj sieci Głównym źródłem podstępu we wszystkich wczesnych próbach ułatwienia obsługi klienta po stronie serwera i twórców aplikacji zależnych od tego krytycznego punktu. Żądania HTTP nie są i nigdy nie były skomplikowane. Nie wymagały warstwy 18! @ # $ Ing mylącej potwory-nazwa-zdarzenia-na-każdym etapie, aby ułatwić zrozumienie. Podobnie, wiele można wiedzieć o stronie klienta, ale nie ma powodu, aby ukrywać się przed HTML i DOM, który wchodzi w interakcję z nim, klepiąc na nim duży gigantyczny model. Jest to już duży gigantyczny model i działa bardzo dobrze. Wszystko, czego potrzebujemy, aby było nieco łatwiejsze w zarządzaniu, to rozsądne praktyki OOP oraz trochę wiedzy na temat JS i DOM.

  • Sprzyjaj elastyczności

EXTjs <==== elastyczność skali ====> jQuery (niekoniecznie żadna z jej wtyczek)

IMO, narzędzia, które pozwalają szybko wykonać majsterkowanie, są zawsze lepszym wyborem. Narzędzia, które zrobiły to wszystko za Ciebie, są właściwym wyborem, gdy nikt ponad twoją głową nie jest szczególnie wybredny w kwestii szczegółów i nie masz nic przeciwko przekazaniu kontroli nad tym, co powinno ci pomóc. Rzeczywiście widziałem wtyczki, które sprawdzają poprawność HTML, aby upewnić się, że nie wkradasz się do innego rodzaju elementu z tymi samymi dokładnymi cechami wyświetlania. Czemu? Mam tylko teorie. Wydaje mi się, że sprowadza się to do tego, że autorzy naprawdę nienawidzą pomysłu, aby ktoś używał swoich rzeczy w sposób, który nie był zamierzony, i to zawsze nieuchronnie to, co ktoś chce, abyś robił w interfejsie użytkownika.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.