Po pierwsze, zanim ktokolwiek krzyknie oszukany, ciężko mi było podsumować to w prostym tytule. Inny tytuł mógłby brzmieć „Jaka jest różnica między modelem domeny a modelem MVC?” lub „Co to jest model?”
Koncepcyjnie rozumiem, że Model to dane używane przez widoki i kontroler. Poza tym wydaje się, że istnieje wiele różnych opinii na temat tego, co składa się na model. Co to jest model domeny, model aplikacji, model widoku, model usług itp.
Na przykład w ostatnim pytaniu, które zadałem na temat wzorca repozytorium, powiedziano mi wprost, że repozytorium jest częścią modelu. Czytałem jednak inne opinie, że model powinien być oddzielony od modelu trwałości i warstwy logiki biznesowej. W końcu czy wzorzec repozytorium nie powinien oddzielać konkretnej metody trwałości od modelu? Inni twierdzą, że istnieje różnica między modelem domeny a modelem MVC.
Weźmy prosty przykład. AccountController, który jest dołączony do domyślnego projektu MVC. Czytałem kilka opinii, że kod konta jest kiepskiej konstrukcji, narusza SRP itp. Itd. Gdyby ktoś miał zaprojektować „właściwy” model członkostwa dla aplikacji MVC, co by to było?
W jaki sposób można oddzielić usługi ASP.NET (dostawca członkostwa, dostawca ról itp.) Od modelu? A może w ogóle byś?
Z mojego punktu widzenia model powinien być „czysty”, być może z logiką walidacji .. ale powinien być oddzielony od reguł biznesowych (inny niż walidacja). Na przykład, powiedzmy, że masz regułę biznesową, która mówi, że ktoś musi otrzymać e-maila, gdy zostanie utworzone nowe konto. Moim zdaniem to nie pasuje do modelu. Więc gdzie to należy?
Czy ktoś chciałby rzucić trochę światła na tę kwestię?
źródło
Odpowiedzi:
Sposób, w jaki to zrobiłem - i nie twierdzę, że jest to dobre czy złe, polega na posiadaniu mojego Poglądu, a następnie modelu, który odnosi się do mojego poglądu. Ten model ma tylko to, co jest istotne dla mojego widoku - w tym adnotacje danych i reguły walidacji. Sterownik zawiera tylko logikę do budowy modelu. Mam warstwę usług, która zawiera całą logikę biznesową. Moi kontrolerzy nazywają moją warstwę usług. Poza tym jest moja warstwa repozytorium.
Moje obiekty domeny są przechowywane osobno (w rzeczywistości we własnym projekcie). Mają własne adnotacje danych i reguły walidacji. Moje repozytorium sprawdza poprawność obiektów w mojej domenie przed zapisaniem ich w bazie danych. Ponieważ każdy obiekt w mojej domenie dziedziczy po klasie bazowej, która ma wbudowaną walidację, moje repozytorium jest generyczne i sprawdza wszystko (i wymaga, aby dziedziczyło z klasy bazowej).
Można by pomyśleć, że posiadanie dwóch zestawów modeli jest powielaniem kodu i to do pewnego stopnia. Ale są całkiem rozsądne przypadki, w których obiekt domeny nie jest odpowiedni dla widoku.
Przykładem może być praca z kartami kredytowymi - muszę wymagać CVV podczas przetwarzania płatności, ale nie mogę go przechowywać (jest to grzywna w wysokości 50 000 USD). Ale chcę również, abyś mógł edytować swoją kartę kredytową - zmienić adres, nazwisko lub datę ważności. Ale nie podasz mi numeru ani CVV podczas edycji, a na pewno nie zamierzam umieszczać numeru Twojej karty kredytowej zwykłym tekstem na stronie. Moja domena ma te wartości wymagane do zapisania nowej karty kredytowej, ponieważ mi je przekazujesz, ale mój model edycji nie zawiera nawet numeru karty ani numeru CVV.
Inną korzyścią płynącą z tak wielu warstw jest to, że po prawidłowej architekturze można używać mapy struktury lub innego kontenera IoC i wymieniać elementy bez szkody dla aplikacji.
Moim zdaniem kod kontrolera powinien być tylko kodem ukierunkowanym na widok. Pokaż to, ukryj itd. Warstwa usług powinna zawierać logikę biznesową dla Twojej aplikacji. Lubię mieć to wszystko w jednym miejscu, więc łatwo jest zmienić lub dostosować regułę biznesową. Warstwa repozytorium powinna być stosunkowo głupia - pozbawiona logiki biznesowej i tylko wysyłać zapytania do danych i zwracać obiekty domeny. Oddzielając modele widoku od modelu domeny, masz znacznie większą elastyczność, jeśli chodzi o niestandardowe reguły walidacji. Oznacza to również, że nie musisz zrzucać wszystkich danych do widoku w ukrytych polach i przepychać je tam iz powrotem między klientem a serwerem (lub odbudowywać na zapleczu).
<% if (!String.IsNullOrEmpty(Model.SomeObject.SomeProperty) && Model.SomeObject.SomeInt == 3 && ...) { %>
Chociaż wszystko wydaje się rozłożone i wielowarstwowe, ma cel, aby być tak zaprojektowane. Czy to jest doskonałe? nie całkiem. Ale wolę to od niektórych wcześniejszych projektów wywoływania repozytoriów ze sterownika i mieszania logiki biznesowej w kontrolerze, repozytorium i modelu.
źródło
Zbyt często zastanawiałem się, jak dokładnie elementy MVC pasują do tradycyjnej struktury aplikacji internetowej, w której masz widoki (strony), kontrolery, usługi i obiekty danych (model). Jak powiedziałeś, istnieje wiele wersji tego.
Uważam, że zamieszanie istnieje z powodu wyżej wymienionej, szeroko akceptowanej architektury, która wykorzystuje „model domeny anemicznej” (rzekomy) wzorzec -anti. Nie będę wchodził w szczegóły na temat „anty-wzorcowości” anemicznego modelu danych (możesz spojrzeć na mój wysiłek, aby wyjaśnić to tutaj (oparty na Javie, ale odpowiedni dla każdego języka)). Krótko mówiąc, oznacza to, że nasz model przechowuje tylko dane, a logika biznesowa jest umieszczona w usługach / menedżerach.
Ale załóżmy, że mamy architekturę opartą na domenach , a nasze obiekty domeny są takie, jakich się oczekuje - mają zarówno stan, jak i logikę biznesową. I w tej opartej na domenie perspektywie rzeczy mają miejsce:
Myślę, że to odpowiada na twoje główne pytania. Sytuacja komplikuje się, gdy dodajemy więcej warstw, takich jak warstwa repozytorium. Często sugeruje się, że powinno być wywoływane przez logikę biznesową umieszczoną w modelu (stąd każdy obiekt domeny ma odniesienie do repozytorium). W moim artykule, który zamieściłem, stwierdzam, że nie jest to najlepsza praktyka. I rzeczywiście, posiadanie warstwy usług nie jest złe. Nawiasem mówiąc, projektowanie oparte na domenach nie wyklucza warstwy usług, ale ma być „cienkie” i tylko koordynować obiekty domeny (więc nie ma tam logiki biznesowej).
W przypadku anemicznego paradygmatu modelu danych, który jest powszechnie przyjęty (na dobre lub na złe), model byłby zarówno warstwą usług, jak i obiektami danych.
źródło
W mojej opinii,
Model -
Nie powinien zawierać logiki biznesowej, powinien być dołączalny (scenariusz podobny do programu WCF). Jest używany do łączenia się z widokiem, więc powinien mieć właściwości.
Logika biznesowa -
Powinien być umieszczony w „warstwie usług domenowych”, jest to całkowicie oddzielna warstwa. Ponadto doda tutaj jeszcze jedną warstwę „Usługi aplikacji”.
App Services komunikuje się z warstwą usług domenowych w celu zastosowania logiki biznesowej, a następnie na koniec zwraca model.
Tak więc kontroler poprosi usługę aplikacji o model, a przepływ będzie wyglądał następująco:
źródło
Wzorzec MVC i framework Asp.net nie rozróżniają, jaki powinien być model.
Własne przykłady MS obejmują klasy trwałości w modelu. Twoje pytanie o członkostwo w modelu. To zależy. Czy klasy w twoim modelu są przez coś własnością? Czy istnieje powiązanie między tym, kto się loguje, a jakie dane są wyświetlane? Czy istnieje filtrowanie części danych w systemie uprawnień, którą można edytować? Kto ostatnio aktualizował lub edytował obiekt w Twojej domenie, ponieważ ktoś inny musi go zobaczyć, czy coś do obsługi zaplecza?
Przykład e-maila też zależy. Czy znasz szczególnie zdarzenie w domenie lub wydarzenie? Czy masz oddzielną usługę do wysyłania e-maili? Czy wysyłanie wiadomości e-mail jest częścią Twojej domeny, czy jest to problem na poziomie aplikacji poza zakresem Twojego systemu? Czy interfejs użytkownika musi wiedzieć, czy wiadomość e-mail została wysłana pomyślnie, czy nie? Czy wiadomości e-mail, których nie udało się wysłać, wymagają ponownych prób? Czy treść wysłanej wiadomości e-mail musi być przechowywana na potrzeby pomocy technicznej lub obsługi klienta?
Tego typu pytania są zbyt obszerne i subiektywne, ale odpowiadam, abyście Ty i wszyscy, którzy głosowali na was, mogliście to zrozumieć.
Twoje wymagania / terminy / zasoby przenikają do architektury twojego systemu. Nawet model przychodów może mieć wpływ. Musisz także wziąć pod uwagę wzór, do którego strzelasz. DDD różni się znacznie od aplikacji typu persence-as-model, a wszystkie przerwy między nimi są również ważne dla niektórych aplikacji. Czy strzelasz do testowania aplikacji? Wszystko to ma wpływ.
źródło