asp.net MVC - dalszy rozwój

0

Witajcie

Kilka słów wstępu i prywaty, żeby rozjaśnić temat:
Koduje już w MVC od roku, a w ogólnie w C# od 1,5 roku (w firmie gdzie pracuje) - podjąłem decyzję że z Webformsów, w których były realizowane projekty, przejdę sobie na MVC (i sam się tego MVC zacząłem uczyć).

Pamiętam moje pierwsze projekty w MVC- ciężki kod, wszystko nabite w kontrolerach, mnóstwo kodu w widokach, użyty "na ślepo" EF. Jakoś to działało, spełniało wymagania, MVC spodobało mi się i zostałem przy tym.

Teraz po roku i kilku zrealizowanych komercyjnych projektach tworze kod zgodnie z ze wzorcami i regułami IoC/UoW itp. Czasami z nudów sięgnę po NHibernate zamiast EF. Poradzę sobie przy jakiś stosunkowo złożonych funkcjach i bajerach w jQuery. Porównując swoje "osiagnięcia" i wiedzę z przed roku, a wiedzę teraz - jest ogromna różnica.

Czasami odczuwam że coś mi brakuje (ale to chyba normalne u programistów). Tzn, że coś mógłbym jeszcze się nauczyć i wdrożyć, coś zastosować itp.

W związku z tym, mam rzeczowe pytanie - co mogę jeszcze zrobić? Skąd czerpać dalszą wiedzę? W którym kierunku coś rozwinąć jeszcze itp.

Przykład - nie stosuje w ogóle testów. Nie wiem dlaczego, nie było jakoś nigdy potrzeby albo czasu na pisanie tych testów. Teraz chciałbym to zmienić. Ale jakie testy pisać, w jakiej ilości żeby nie przesadzić, co warto a co nie warto pokryć testami itp.

Podejrzewam że takich obszarów do poduczenia się jest więcej. W związku z tym, zależałoby mi na odpowiedziach jak to wygląda z Waszej perspektywy i doświadczenia i pod kątem większych projektów. Chce po prostu postawić dalszy krok w rozwoju, żeby nie stać w miejscu.

Pzdr

1
franck napisał(a):

Przykład - nie stosuje w ogóle testów. Nie wiem dlaczego, nie było jakoś nigdy potrzeby albo czasu na pisanie tych testów. Teraz chciałbym to zmienić. Ale jakie testy pisać, w jakiej ilości żeby nie przesadzić, co warto a co nie warto pokryć testami itp.

Musisz mieć bardzo dużo wolnego czasu i kochać debugowanie godzinami, skoro nie pisesz testów. :P

Podejrzewam że takich obszarów do poduczenia się jest więcej. W związku z tym, zależałoby mi na odpowiedziach jak to wygląda z Waszej perspektywy i doświadczenia i pod kątem większych projektów. Chce po prostu postawić dalszy krok w rozwoju, żeby nie stać w miejscu.

Czy korzystasz z WCF?
Jakiej biblioteki używasz do logowania?
Jak mapujesz encje na viewmodele?
Czy stosujesz DDD?
Czy korzystasz prawidłowo z wzorca repozytorium?
Jak rozwiązujesz problem stronicowania wyświetlanych wyników?
Czy jesteś w stanie, w dowolnej chwili zmienić w swojej aplikacji ASP.NET MVC na interfejs konsolowy, bez ruszania kodu realizującego proces biznesowy?

1

Testy powinny pokrywac calosc projektu i wszystkie warstwy, jednak wiadomo, ze czesto jest to zalozenie utopijne.
Logika biznesowa IMO zawsze powinna byc pokryta testami (zarowno jednostkowymi jak i integracyjnymi), testy sa te najszybsze zreszta do napisania i czesto nie wymagaja mockowania.
Do pozostalych warstw mozna przyjac zalozenie, zeby przynajmniej pisac testy tam, gdzie koszt czasu przy debugowaniu + ryzyko zepsucia czegos podczas refaktoringu jest na tyle wysokie, ze uzasadnia to napisanie testow.
Do tego co napisal somekind ja dodam od siebie:
a) do MVC

  • naucz sie dobrze pisac helpery
  • uzywaj helperow przez siebie zdefiniowanych zamiast domyslnych. Jezeli klient sobie zazyczy, ze przy kazdym textboxie ma byc jakas ikonka, nie bedziesz musial z ręki zmieniac wszystkich widokow po kolei, a zmodyfikujesz kod helpera. (traktuj helpery jak kontrolki z webforms)
  • korzystaj z wzorca PRG
    b) Ogolne
  • Zapoznaj sie z wzorcami CQRS oraz domain events. Nie mowie ze wszystko to jest idealne i trzeba zawsze stosowac. Warto jednak znac i miec pod reka klocki, ktore beda sie nadawac do problemow okreslonego rodzaju.
  • Czy jezeli jakas zewn. biblioteka, ktora sciagnales np. nugetem okaze sie, ze posiada niedopuszczalna dziure lub po prostu istnieje potrzeba zrezygnowania z niej, to czy w prosty nieinwazyjny sposob jestes w stanie podmienic ja na inna bez robienia rewolucji w kodzie?
  • pisz kod, aby czesc logiki byly konfigurowalna, np. zalezna od firmy, ktora ja stosuje
  • pisz kod tak, aby drobna zachianka klienta nie wymagala Ctrl+F i replace all. (komponenty aplikacji oparte o interfejsy i abstrakcje, wieksza ziarnistosc klas, kod ma byc krotki i czytelny, jezeli widzisz ze jakas metoda jest dluga to wysoce prawodpodobne, ze mogles zaprojektowac to lepiej i zaleznosci pomiedzy komponentami oraz architektura wymagaja ponownego przeanalizowania),
  • pisz kod tak, aby logika byla wymienialna, a nie zdefiniowana na stale w obiekcie domeny (np. generowanie numeru zamowienia). Stosuj wzorzec strategii.
    Nawet takie banalne obliczenia jak wyliczenie ceny brutto okazuje sie, ze moze odbywac sie na dwa rozne sposoby.

A tak całkiem już ogólnie - sprobuj zawsze z wyprzedzeniem przewidywac decyzje klienta. W miare postepu czesciej bedziesz czul, ze modyfikacje, nowe funkcjonalnosci nie wymagaja duzych zmian w kodzie - to oznacza, ze coraz lepiej idzie Ci pisanie aplikacji.

0

Co prawda nie mój wątek, ale podczepię się.

Jak mapujesz encje na viewmodele?

Podejścia, które są mi znane to "opakowanie" encji przez get/set, bez opakowywania encji, lecz z kopiowaniem, czy to w jedną stronę, czy w drugą stronę, a trzecie to automappery, do których jakoś nie mam przekonania. Pierwszy sposób chyba nadaje się do mniejszych projektów, gdzie zazwyczaj do view i tak trafia wszystko jak leci z bazy. Jakie jest Twoje podejście @somekind?

Czy korzystasz prawidłowo z wzorca repozytorium?

Ja korzystam w pracy raczej nieprawidłowo bo w pracy stosujemy generyczne repozytoria, które są uznawane, z tego co mi wiadomo, za antywzorzec ;) Projekt nie jest specjalnie duży i szczerze nawet używa się tego ok - chociaż nie za bardzo nie interesowałem się jak można do tego inaczej podejść.

0
ikolp napisał(a):

Podejścia, które są mi znane to "opakowanie" encji przez get/set, bez opakowywania encji, lecz z kopiowaniem, czy to w jedną stronę, czy w drugą stronę, a trzecie to automappery, do których jakoś nie mam przekonania. Pierwszy sposób chyba nadaje się do mniejszych projektów, gdzie zazwyczaj do view i tak trafia wszystko jak leci z bazy. Jakie jest Twoje podejście @somekind?

Pierwsze podejście jest mi nieznane, i nie widzę w nim sensu. Przecież o to chodzi z tworzeniem viewmodeli, żeby aplikacja MVC nie operowała na encjach (ani nie miała referencji do dllki z nimi) tylko na viewmodelach właśnie. Natomiast ręczne kopiowanie to strata czasu.
W projektach komercyjnych, w których brałem udział, zazwyczaj używany jest AutoMapper. Ja go osobiście nie lubię, bo wymaga konfigurowania, a ja tego bardzo nie lubię. W swoich projektach używam ValueInjectera, bo on po prostu działa.
A czemu nie jesteś przekonany do automapperów?

Ja korzystam w pracy raczej nieprawidłowo bo w pracy stosujemy generyczne repozytoria, które są uznawane, z tego co mi wiadomo, za antywzorzec ;) Projekt nie jest specjalnie duży i szczerze nawet używa się tego ok - chociaż nie za bardzo nie interesowałem się jak można do tego inaczej podejść.

Repozytoria nabierają sensu w projektach, w których stosuje się (a przynajmniej stara się stosować) DDD. Tylko trzeba przestać myśleć, że repozytoria to element warstwy dostępu do danych, i zrozumieć, że jest to logika biznesowa. Interfejsy repozytoriów wsadza się do tego samego projektu, w którym mamy encje i serwisy biznesowe, które z nich korzystają. Jak już skończymy pisać otestowaną domenę w ten sposób, bierzemy się za jakąś konkretną implementację repozytoriów w oparciu o taką technologię dostępu do danych, jaka nam pasuje.

0

Generyczne repozytorium samo z siebie nie jest antywzorcem (przecież IDBSet<T> z EF to też generyczne repo). Natomiast używane nieprawidłowo faktycznie jest antywzorcem.

0
micc napisał(a):

Generyczne repozytorium samo z siebie nie jest antywzorcem (przecież IDBSet<T> z EF to też generyczne repo). Natomiast używane nieprawidłowo faktycznie jest antywzorcem.

Jest antywzorcem samo z siebie, bo generyczne repozytorium to nie jest w ogóle repozytorium. Repozytorium ma mieć znaczenie biznesowe i ma definiować kontrakt dla innych obiektów, które z niego korzystają. Generyczne repozytorium tego nie robi.
http://www.ben-morris.com/why-the-generic-repository-is-just-a-lazy-anti-pattern

IDBSet<T> to nie jest żadne repozytorium tylko generyczny obiekt dostępu do danych, czyli DAO.

0

Zgadza się, formalnie rzecz biorąc, implementacja IDbSet nie jest wzorcem repozytorium. Zastosowałem uproszczenie, ponieważ większość osób stosuje generyczne repozytoria do prostych operacji CRUD na bazie danych (de facto są to wrappery na IDbSet). Stosowanie generic repository bezpośrednio jest złe, natomiast nie widzę przeciwwskazań, żeby wyodrębnić sobie klasę implementującą interfejs wspólnych operacji dla pewnego podzbioru obiektów biznesowych i dziedziczenie go przez konkretne repozytoria.

0
micc napisał(a):

Zastosowałem uproszczenie, ponieważ większość osób stosuje generyczne repozytoria do prostych operacji CRUD na bazie danych (de facto są to wrappery na IDbSet).

Czyli ich generyczne repozytoria nie są repozytoriami tylko wrapperami na obiekty DAO, a więc też obiektami DAO. Nazywanie czegoś takiego repozytorium jest błędem. Bardzo powszechnym, ale błędem, i trzeba z tym walczyć.

Stosowanie generic repository bezpośrednio jest złe, natomiast nie widzę przeciwwskazań, żeby wyodrębnić sobie klasę implementującą interfejs wspólnych operacji dla pewnego podzbioru obiektów biznesowych i dziedziczenie go przez konkretne repozytoria.

I z tym się zgadzam. Oczywiście taka klasa musi być w warstwie perzystencji, a nie logiki biznesowej - ta operuje wyłącznie na interfejsach repozytoriów dla konkretnych encji.

0

Super zapłon, ale.. zapomniałem sobie o moim topicku :D Pamiętam że najpierw byłem na nartach 1,5 tyg, a później rozłożyła mnie grypa na ponad tydzień i wyleciało mi to totalnie z głowy. Dziś znów rozmyślałem sobie o samorozwoju zawodowym i przypomniałem sobie o istnieniu tego tematu ;)

@somekind

Czy korzystasz z WCF?

W samych projektach z użyciem MVC nie. Ale napisałem kiedyś 2-3 serwery (jak można to tak nazwać) w oparciu o WCF (crossplatformowy komunikator oparty na protokole http), serwer do zwracania danych o kliencie (podpięcie innego systemu) i jakieś mniejsze pierdoły :P anyway - tak jak na początku napisałem - wewnątrz projektu nie korzystam - a jeżeli powinienem, to podaj proszę przykład o co kaman, bo poza SOA nie widzę sensu łączenia wcf+mvc (o SOA też słyszałem, nie używam tego).

Jakiej biblioteki używasz do logowania?

Nlog

Jak mapujesz encje na viewmodele?

Automapper r0x

Czy stosujesz DDD?

Nie, i tutaj chciałbym się podszkolić bardzo - możesz mi podrzucić jakieś materiały fajne na ten temat? Gdzie nie poszukam, tam jakieś łyse formułki.

Czy korzystasz prawidłowo z wzorca repozytorium?

Z tego co widzę po postach - to korzystam nieprawidłowo bo używam własnego generycznego repozytorium :) Był czas, gdzie dla każdego modelu(encji) miałem osobne repozytorium, ale gdy projekt zaczął rosnąć szybko, powstawało wiele modeli gdzie poza 4 podstawowymi metodami (CRUD) nie potrzebowałem niczego więcej, napisałem sobie generyczne repo. Rozumiem że poprawne jest podejście, gdzie dla każdego modelu jest osobne(dedykowane) repozytorium?

Jak rozwiązujesz problem stronicowania wyświetlanych wyników?

Dziele sobie liczbę wyników przez jakiś interwał, np 10, zaokrąglam do góry => mam liczbę stron. Następnie prostymi funkcjami .Skip() i .Take() biorę co mi potrzeba :)

Czy jesteś w stanie, w dowolnej chwili zmienić w swojej aplikacji ASP.NET MVC na interfejs konsolowy, bez ruszania kodu realizującego proces biznesowy?

Tak, kontrolery u mnie poza przechwytywaniem informacji i odbieraniem danych od logiki biznesowej nie robią nic kluczowego. A logika biznesowa jest wydzielona od warstwy prezentacji.

@micc

korzystaj z wzorca PRG

korzystam :)

naucz sie dobrze pisac helpery

pisać potrafię i używam czasami swoich helperów (np własny helper kalendarza)

  • Zapoznaj sie z wzorcami CQRS oraz domain events. Nie mowie ze wszystko to jest idealne i trzeba zawsze stosowac. Warto jednak znac i miec pod reka klocki, ktore beda sie nadawac do problemow okreslonego rodzaju.

można prosić o materiały jakieś na ten temat w miarę sensowne? :)

Czy jezeli jakas zewn. biblioteka, ktora sciagnales np. nugetem okaze sie, ze posiada niedopuszczalna dziure lub po prostu istnieje potrzeba zrezygnowania z niej, to czy w prosty nieinwazyjny sposob jestes w stanie podmienic ja na inna bez robienia rewolucji w kodzie?

raczej tak, kilka zmian będzie, ale większej rewolucji nie widzę.

Z góry dzięki za odpowiedzi.

0
franck napisał(a):

W samych projektach z użyciem MVC nie. Ale napisałem kiedyś 2-3 serwery (jak można to tak nazwać) w oparciu o WCF (crossplatformowy komunikator oparty na protokole http), serwer do zwracania danych o kliencie (podpięcie innego systemu) i jakieś mniejsze pierdoły :P anyway - tak jak na początku napisałem - wewnątrz projektu nie korzystam - a jeżeli powinienem, to podaj proszę przykład o co kaman, bo poza SOA nie widzę sensu łączenia wcf+mvc (o SOA też słyszałem, nie używam tego).

W przypadku dużych systemów jest tak, że ich warstwy należy umieszczać na oddzielnych fizycznych maszynach, w celu zwiększenia wydajności. W takim przypadku ukrycie logiki biznesowej pod WCF i oddzielenie tego od aplikacji MVC zajmującej się tylko prezentacją ma sens.

Nie, i tutaj chciałbym się podszkolić bardzo - możesz mi podrzucić jakieś materiały fajne na ten temat? Gdzie nie poszukam, tam jakieś łyse formułki.

Może to: http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215

Z tego co widzę po postach - to korzystam nieprawidłowo bo używam własnego generycznego repozytorium :) Był czas, gdzie dla każdego modelu(encji) miałem osobne repozytorium, ale gdy projekt zaczął rosnąć szybko, powstawało wiele modeli gdzie poza 4 podstawowymi metodami (CRUD) nie potrzebowałem niczego więcej, napisałem sobie generyczne repo. Rozumiem że poprawne jest podejście, gdzie dla każdego modelu jest osobne(dedykowane) repozytorium?

Tak, ale tu nie chodzi o osobne repozytoria, tylko o to, aby encje i serwisy domenowe korzystały z kontraktów repozytoriów, a nie konkretnych implementacji. Ten kontrakt możesz później zaimplementować dziedzicząc jedynie z generycznego repozytorium i nie dopisywać do niego żadnego kodu, jeśli nie trzeba, i będzie ok.

Dziele sobie liczbę wyników przez jakiś interwał, np 10, zaokrąglam do góry => mam liczbę stron. Następnie prostymi funkcjami .Skip() i .Take() biorę co mi potrzeba :)

To jasne, ale mi chodziło o całość, czyli jak to realizujesz od GUI do bazy i z powrotem.

0

W przypadku dużych systemów jest tak, że ich warstwy należy umieszczać na oddzielnych fizycznych maszynach, w celu zwiększenia wydajności. W takim przypadku ukrycie logiki biznesowej pod WCF i oddzielenie tego od aplikacji MVC zajmującej się tylko prezentacją ma sens.

Tak, i mam w planach zrobić sobie taką własną apkę (jakiś portal ogłoszeniowy/aukcyjny - dla nauki) oparty na SOA i rozproszony na 2-3 maszynach fizycznych :) W pracy tego nie stosuje, bo projekty nie są aż tak duże, żeby rozdzielać warstwy, ale wiem o co chodzi w tym plus minus.

Może to: http://www.amazon.com/Domain-D[...]plexity-Software/dp/0321125215
Co do DDD, mozesz zaczac tu:

@somekind & @n0name_l - dzięki

Tak, ale tu nie chodzi o osobne repozytoria, tylko o to, aby encje i serwisy domenowe korzystały z kontraktów repozytoriów, a nie konkretnych implementacji. Ten kontrakt możesz później zaimplementować dziedzicząc jedynie z generycznego repozytorium i nie dopisywać do niego żadnego kodu, jeśli nie trzeba, i będzie ok.

Ok, ale mimo wszystko używanie kontraktu IRepository<JakaśEncja> jest antywzorcem, tak? Zamiast tego powinna być klasa generyczna Repository<T>, po której dziedziczy klasa JakaśEncjaRepo i dopiero wtedy po kontrakcie IJakaśEncjaRepo, możemy się odwoływać do metod w klasie bazowej - Repository<T> , jeżeli nie potrzebujemy nic więcej niż podstawowe metody CRUD - mam rację?

To jasne, ale mi chodziło o całość, czyli jak to realizujesz od GUI do bazy i z powrotem.

Nazwy zmyślone i bezsensu:
Mam PartialView z listą stron. Wywołuje go z metodą z kontrolera (helper RenderAction). Załóżmy że metoda nazywa się Paginator. Następnie w tej metodzie, korzystam z jakiegoś kontraktu biznesowego - np IArticleBase. Poprzez metodę GetPagesCount(), zwracam sobie do kontrolera liczbę iteracji. Następnie w tym widoku, generuje sobie odpowiednie linki do paginacji prostą pętlą + dodaję querystring page=1/2/3/4... Thats all, jeżeli chodzi o "paginację". Następnie w widoku listy artykułów, po wywołaniu kontrolera z odpowiednim querystringiem (page) biorę z kontraktu IArticleBase metodę GetArticles(int page) i voila - otrzymuję w kontrolerze listę np. 10 artykułów, od razu zmapowanych na viewmodele. Takie podejście jest okej? Stosuje je zawsze praktycznie.

Mam pytanie - co jeszcze u siebie rozwijać? Nie wiem, np. zmianę ORMu na Nhibernate przy 1-2 projektach? Oprócz DDD i wspomnianych wcześniej rad, na czym warto się skupić? Czuje że mam pewien poziom za sobą i nie wiem w co iść żeby zdobyć kolejny level.

Pozdr!

2
franck napisał(a):

Ok, ale mimo wszystko używanie kontraktu IRepository<JakaśEncja> jest antywzorcem, tak? Zamiast tego powinna być klasa generyczna Repository<T>, po której dziedziczy klasa JakaśEncjaRepo i dopiero wtedy po kontrakcie IJakaśEncjaRepo, możemy się odwoływać do metod w klasie bazowej - Repository<T> , jeżeli nie potrzebujemy nic więcej niż podstawowe metody CRUD - mam rację?

Z grubsza tak, ale opiszę dokładniej, żeby nie było niejasności.

Mamy sobie projekt "BusinessLogic", w którym trzymamy nasze encje, serwisy i repozytoria. Mamy tam IRepository<TEntity> z metodami:
Add(TEntity entity)
Delete(int id)
Update(TEntity)
Get(int id)
GetAll()
itd...

Mamy IStudentsRepository : IRepository<Student> z dodatkową metodą np.: GetStudentsAboveAverage(double average). Z niego korzystamy w całym tym projekcie, gdy chcemy się dobrać do studentów.

Mamy też projekt Persistence, w którym mamy RepositoryBase : IRepository<TEntity> z implementacją jego metod. (To tylko taka przykrywka na EF, NH czy jeszcze coś innego).
Mamy też StudentsRepository : RepositoryBase<Student>, IStudentsRepository i tutaj musimy zaimplementować GetStudentsAboveAverage(double average), bo resztę metod mamy z repo bazowego.

IoC konfigurujemy tak, aby IStudentsRepository było implementowane przez StudentsRepository i to wszystko. Logika biznesowa jest pozbawiona patologii i nie korzysta z żadnego generycznego repozytorium.

Mam PartialView z listą stron. Wywołuje go z metodą z kontrolera (helper RenderAction). Załóżmy że metoda nazywa się Paginator. Następnie w tej metodzie, korzystam z jakiegoś kontraktu biznesowego - np IArticleBase. Poprzez metodę GetPagesCount(), zwracam sobie do kontrolera liczbę iteracji. Następnie w tym widoku, generuje sobie odpowiednie linki do paginacji prostą pętlą + dodaję querystring page=1/2/3/4... Thats all, jeżeli chodzi o "paginację". Następnie w widoku listy artykułów, po wywołaniu kontrolera z odpowiednim querystringiem (page) biorę z kontraktu IArticleBase metodę GetArticles(int page) i voila - otrzymuję w kontrolerze listę np. 10 artykułów, od razu zmapowanych na viewmodele. Takie podejście jest okej? Stosuje je zawsze praktycznie.

I wszystko po stronie klienta piszesz sam, nie używasz żadnego gotowego Grida?
Abstrahując - ja w kontrolerze odbieram żądanie z widoku, i buduję sobie obiekt typu PagingRequest<T>, ma on właściwości: PageNumber i PageSize, obiekt ten wysyłam do serwisu, który zwraca mi obiekt PageResult<T> z właściwościami PageNumber, PageSize, TotalCount IList<T> Items. Na tej podstawie łatwo już wyświetlić tabelkę z danymi i zbudować pager (albo zasilić grida).

Mam pytanie - co jeszcze u siebie rozwijać? Nie wiem, np. zmianę ORMu na Nhibernate przy 1-2 projektach?

Inny ORM, inny logger, inny kontener IoC, inna architektura, inny framework.

1 użytkowników online, w tym zalogowanych: 0, gości: 1