Dobre praktyki .NET

0

Cześć, piszę sobie już od jakiegoś roku w ASP.NET MVC i chciałbym dopytać się Was o dobre praktyki.
Dążę do tego żeby mój kod był jak najlepszej jakości, dlatego chciałbym was prosić o wskazanie popularnych błędów nowicjuszy i zaproponowanie dobrych zasad których warto według was przestrzegać. Chętnie wysłucham opinii starszych kolegów chociażby byłby to proste komentarze typu "pisz mało kodu w kontrolerze, logikę umieszczaj w modelu".

2

pisz mało kodu w kontrolerze, logikę umieszczaj w modelu

2

SOLID, YAGNI, DRY, GRASP, Separation of concerns.

6
  1. Zrób z modelu osobny projekt;
  2. Twórz w modelu mikroserwisy dziedziczące po interfejsach, które samodzielnie zdefiniujesz. Po co to jest? Patrz punkt 4 oraz dodatkowo łatwiejsze testowanie modelu i mockowanie serwisów;
  3. Twórz logikę biznesową w tychże mikroserwisach;
  4. Używaj kontenera IoC żeby zbindować interfejsy do odpowiednich mikroserwisów, żeby nie musieć bawić się w ręczne wstrzykiwanie zależności, np. do kontrolerów;
  5. Nie czytaj hinduskich tutoriali;
  6. Unikaj staticów czy singletonów na rzecz możliwości ustawienia czasu życia obiektu w kontenerze IoC;
  7. Obiekty z modelu przekazuj do wyższych warstw za pomocą DTO;
  8. Nigdy nie pchaj encji do widoku;
  9. Wzorzec repozytorium: bardzo często niepotrzebny. Można go zastąpić kontekstowym wykorzystaniem ORM'a w serwisach zamiast trzymania globalnego repo albo kilku repo spinanych np. w unit of work. Dla mnie jest to prawie zawsze przerost formy nad treścią;
  10. Nie czytaj hinduskich tutoriali;
  11. Ucz się grać na gitarze samodzielnie jak Jimmy Page.
  12. Nie używaj Entity Frameworka;
  13. Używaj NHibarnate'a albo Dappera;
  14. Kup starego Peugeota.
0

Możesz zaargumentować dlaczego nie używać Entity Framework?

0

http://commitandrun.pl/2016/04/25/Dlaczego_Entity_Framework_nie_jest_dobrym_wyborem/

Poza tym kiedy poużywałem dłużej NHibernate'a to już w sumie mógłbym do EF nie wracać, nie mówiąc już o tym, że EF buduje koszmarne zapytania i jest naprawdę powolny w stosunku do chyba całej reszty ORM'ów.

PS: Dalej jest tam tylko left join? Już nawet sam sposób w jaki zapisuje się left joina w EF (bo nie można po prostu napisać .Left.Join(), prawda?) plus stosowanie różnych includów i mamy niezły ROTFL. Nie to co prostota NH, choć przyznaję: NH ma troszkę większy próg wejścia niż EF ale później oddaje to z nawiązką.

0
grzesiek51114 napisał(a):

http://commitandrun.pl/2016/04/25/Dlaczego_Entity_Framework_nie_jest_dobrym_wyborem/

Poza tym kiedy poużywałem dłużej NHibernate'a to już w sumie mógłbym do EF nie wracać, nie mówiąc już o tym, że EF buduje koszmarne zapytanie i jest naprawdę powolny w stosunku do chyba całej reszty ORM'ów.

PS: Dalej jest tam tylko left join? Już nawet sam sposób w jaki zapisuje się left joina w EF plus stosowanie różnych includów i mamy niezły ROTFL. Nie to co prostota NH, choć przyznaję: NH ma troszkę większy prób wejścia niż EF ale później oddaje to z nawiązką.

U mnie w firmie stosuje sie w EF i raczej narzekaja na NH, ale dzieki za info, bede musial sie zapoznac rowniez z NH i potestowac :)

0

@Smutny Orzeł
Polecam. Poza tym od dawna odnoszę dziwne wrażenie, że w EF dużo łatwiej polecieć z N+1 select'em, co jest rzecz jasna kolejną jego wadą.

0

Logika w modelu? Serwisy wstrzykiwane od modeli?
Czy to jest DDD i Domain Services?

Czy kontrolery powinny zwracać te modele czy czyste DTO?

0

Logika w modelu?

Tak. Przecież nie w kontrolerach. :-)

Czy to jest DDD i Domain Services?

A nie wiem. Po prostu takie rozplanowanie projektu jest dla mnie wygodne i racjonalne. Można to nazwać jakkolwiek.

Czy kontrolery powinny zwracać te modele czy czyste DTO?

Komunikacja pomiędzy warstwami modelu za pomocą DTO, a z widokami za pomocą Viewmodeli.

0
grzesiek51114 napisał(a):

Logika w modelu?

Tak. Przecież nie w kontrolerach. :-)

A to nie w warstwie z serwisami? :P

Czy kontrolery powinny zwracać te modele czy czyste DTO?

Komunikacja pomiędzy warstwami modelu za pomocą DTO, a z widokami za pomocą Viewmodeli.
Wszystkie warstwy powinny komunikować się ze sobą tym samym DTO?

Repozytorium(infrastruktura) => Domena => Serwisy => API ten sam DTO?

Czy WebAPI powinno zwracać modele nazwane(zlokalizowane w folderze) DTO czy ViewModel?
Oczywiście przypadki

  1. API dostarcza danych tylko do jednego klienta(frontend) - więc modele są dostosowane do funkcjonalności frontu.
    W takim razie zwraca DTO czy ViewModele?
0

Repozytorium(infrastruktura) => Domena => Serwisy => API ten sam DTO?

Myślę, że tak, bo po co mnożyć byty; ale niech się tutaj wypowie ktoś bardziej doświadczony.

W takim razie zwraca DTO czy ViewModele?

Myślę, że można naprzemiennie. Serwis może zwracać viewmodele.

0

Model... to słowo również zostało zdemoralizowane przez ASP.NET

Albo opierasz swój projekt na DDD i robisz DT + repo ablo na bazodanowych servisach bez repo jeśli jest to prosty CRUD.

DTO używasz kiedy jest porzebne, Jeśli kożystasz z dobrego ORM możesz wypchnąć do warstwy widoku zwykłe POCO a ViewModel używać kiedy musisz zwalidować input.

0

Model to też Servis. Chodzi tu o abstrakcjyny sposób rozwiązania problemu jako całość.

0

A co powiecie o Punkt 1?
Dobrze rozumiem, że w modelach ma być logika(ustawiania pól, akcji) i to te modele mają mieć wstrzyknięte repozytoria?

Zawsze robiłem to tak, że modele to klasy z jakimś tam zachowaniem do zmian wartości pól, warstwa repozytoriów, i warstwa serwisów. I to warstwa serwisów operowała na modelach i wywoływała operacje na repozytoriach.

class DiscountsService
{
	Activate(discountId) 
	{
		discount = discountsRepository.Get(discountId);
		discount.SetStatus(Active);
		discountsRepository.Update(discount);
	}
}

Czy to powinno wyglądać tak:

class Discount 
{
	Discount(IDiscountsRepository discountsRepository) 
	{
		this.discountsRepository = discountsRepository; // DI 
	}
	
	Activate()
	{
		this.Status = Active;
		this.discountsRepository.Save(this);
	}
}
0

Możecie proszę zdefiniować na prostym przykładzie DTO i POCO? Czym różni się od modelu DB i VM?

2

Errata do tego, co @grzesiek51114 napisał.

  1. Nie twórz interfejsów, które niczego nie wnoszą. Nie twórz interfejsu dla każdej klasy (jak w hinduskich tutorialach). Nie twórz interfejsów dla serwisów biznesowych albo klas pomocniczych, bo rzadko kiedy da się uzasadnić ich istnienie. Nie twórz interfejsu do klas, których jesteś autorem, tylko po to, żeby je mockować w testach. Nie bój się swojego kodu i nie bój się uruchamiać swojego kodu w testach.
    Interfejsy twórz tylko tam, gdzie są niezbędne czyli np. są podstawą jakiegoś wzorca projektowego albo do klas odpowiedzialnych za komunikację ze światem zewnętrznym (bazą, plikami, innymi serwisami webowymi) albo wykonujących czasochłonne operacje, które sprawiłyby, że testy jednostkowe trwają za długo.
    A jak dobrze odseparujesz przepływ biznesowy od kontaktów ze światem zewnętrznym, to wtedy nawet tam interfejsów nie będziesz potrzebował, bo logikę przetwarzającą dane przetestujesz jednostkowo bez żadnych mocków, a do testów integracyjnych mocków przecież nie potrzebujesz.

  2. Nie unikaj staticów. Każdą metodę, która może być statyczna rób statyczną, żeby od razu było widać, że nie manipuluje stanem obiektu, więc niczego nie psuje. Każdą klasę zawierającą pomocnicze metody, która nie ma stanu rób statyczną. Unikaj jedynie statycznego stanu, zwłaszcza globalnego do wymiany danych między elementami systemu.

Smutny Orzeł napisał(a):

U mnie w firmie stosuje sie w EF i raczej narzekaja na NH, ale dzieki za info, bede musial sie zapoznac rowniez z NH i potestowac :)

Nie używają, a narzekają? To chyba wiele mówi o psychice...
Problem z NH jest taki, że EF jest dostarczany razem z Visual Studio, a NH trzeba zainstalować, co przekracza możliwości intelektualne 90% programistów.

0
Błękitny Orzeł napisał(a):

A co powiecie o [Punkt 1]commitandrun.pl/2016/05/11/Repozytorium_najbardziej_niepotrzebny_wzorzec_projektowy/)?
Dobrze rozumiem, że w modelach ma być logika(ustawiania pól, akcji) i to te modele mają mieć wstrzyknięte repozytoria?

Zawsze robiłem to tak, że modele to klasy z jakimś tam zachowaniem do zmian wartości pól, warstwa repozytoriów, i warstwa serwisów. I to warstwa serwisów operowała na modelach i wywoływała operacje na repozytoriach.

class DiscountsService
{
	Activate(discountId) 
	{
		discount = discountsRepository.Get(discountId);
		discount.SetStatus(Active);
		discountsRepository.Update(discount);
	}
}

Czy to powinno wyglądać tak:

class Discount 
{
	Discount(IDiscountsRepository discountsRepository) 
	{
		this.discountsRepository = discountsRepository; // DI 
	}
	
	Activate()
	{
		this.Status = Active;
		this.discountsRepository.Save(this);
	}
}

Repo niepowinno zawierać metody Update.

Discount to aggregat...?

To co jest tam źle to to, że robisz coś czego nie rozumiesz, co widać w kodzie.
Raczej idziesz w stronę Table Module i doklejasz do tego na siłę Repo które nim nie jest.

Repo to inny rodzaj abstrakcji pozbawiasz go tego dodając do niego metodę update.

Tego się nieda od tak wytłumaczyć.
A raczej samo tłumaczenie było by trudniejsze niż rozwiązanie problemu.
Poczytaj o TransactionScript, TableModule, Dommain Model, oraz DDD. Ale książki nie jakieś pierdoły z blogów a zwłaszcza MS.

0

Nie używają, a narzekają? To chyba wiele mówi o psychice...
Problem z NH jest taki, że EF jest dostarczany razem z Visual Studio, a NH trzeba zainstalować, co przekracza możliwości intelektualne 90% programistów.

Kiedyś używali, jak jeszcze nie pracowałem w tej firmie.

0
Błękitny Orzeł napisał(a):

Dobrze rozumiem, że w modelach ma być logika(ustawiania pól, akcji) i to te modele mają mieć wstrzyknięte repozytoria?

Absolutnie nie.
Chodzi o to, że najpopularniejszym błędem w stosowaniu repozytoriów jest napisanie sobie warstwy dostępu do danych ze zwykłymi DAO i nazwanie ich repozytoriami, bo tak modnie.
Tymczasem repozytorium to abstrakcja, która z punktu widzenia klas, które jego używają ma działać jak kolekcja encji (a właściwie agregatów) i jest ono nierozerwalnie związane z DDD. Nie masz DDD, nie masz repozytoriów - proste.

Skromny Kret napisał(a):

Poczytaj o TransactionScript, TableModule, Dommain Model, oraz DDD. Ale książki nie jakieś pierdoły z blogów a zwłaszcza MS.

Jakie konkretnie pierdoły i gdzie Ty tam blog MS widzisz?

0

No np. Repo na tuttorialach MS. Oczywiście są godne uwagi oraz polecenia blogi, Lecz na początek polecał bym książki a blogi jako ich rozwinięcie.

Chodziło mi o Tutoriale z MS.

0

A tu pełna zgoda, tutoriale od MS są generalnie słabej jakości.

0
somekind napisał(a):
Błękitny Orzeł napisał(a):

Dobrze rozumiem, że w modelach ma być logika(ustawiania pól, akcji) i to te modele mają mieć wstrzyknięte repozytoria?

Absolutnie nie.
Chodzi o to, że najpopularniejszym błędem w stosowaniu repozytoriów jest napisanie sobie warstwy dostępu do danych ze zwykłymi DAO i nazwanie ich repozytoriami, bo tak modnie.
Tymczasem repozytorium to abstrakcja, która z punktu widzenia klas, które jego używają ma działać jak kolekcja encji (a właściwie agregatów) i jest ono nierozerwalnie związane z DDD. Nie masz DDD, nie masz repozytoriów - proste.

  1. A jeśli repozytorium zwraca i przyjmuje model domenowy to jest w porządku?
  2. Co jest złego w metodzie Update repozytorium? - Na prawdę nie wiem. Jak inaczej to nazwać gdy chcę zapisać zmiany w obiektu?
  3. No skoro repozytorium to abstrakcja warstwy dostępu do danych to czemu nie może żyć bez DDD?
    Mam jeden interfejs IDiscountsRepository i implementacje w zależności od źródła danych InMemoryDiscountsRepository, MicroserviceDiscountsRepository, `DatabaseDiscountsRepository```.

To jest błędne?

  1. Jeśli korzystam z danych pobieranych spoza mojej władzy czyli serwisy innych autorów, firm i nie mam pojęcia gdzie je trzymają(nawet mnie to nie interesuje) to robić repozytorium czy DAO?
0

Jest jakaś aplikacja (z dobrymi praktykami) udostępniona na np. githubie, która jest na tyle rozwinięta, że można zobaczyć w akcji "dobre praktyki"? Czy trzeba wszystko mazać samemu od nowa? Bo tak czytając ten temat, to standardowo wychodzi kłótnia pomiędzy developerami o architekturę przez co informacje są rozmyte w kilkanaście postów :) Sam kiedyś znalazłem jakąś apkę w postaci internetowego sklepu muzycznego czy coś takiego ale nie wiem czy sposób implementacji odpowiada dobrym praktykom.

0
Błękitny Orzeł napisał(a):
  1. A jeśli repozytorium zwraca i przyjmuje model domenowy to jest w porządku?

Jeśli masz DDD i przez model domenowy rozumiesz aggergation root, to tak.

  1. Co jest złego w metodzie Update repozytorium? - Na prawdę nie wiem. Jak inaczej to nazwać gdy chcę zapisać zmiany w obiektu?

A co niby to Update tak konkretnie robi? Wysyła SQL update do bazy?
Bo generalnie do zmiany jakiejś wartości w obiekcie wystarczy użyć settera, a potem po prostu zatwierdzić transakcję w unit of work.

  1. No skoro repozytorium to abstrakcja warstwy dostępu do danych to czemu nie może żyć bez DDD?

Bo zostało zdefiniowane jako część DDD.

Mam jeden interfejs IDiscountsRepository i implementacje w zależności od źródła danych InMemoryDiscountsRepository, MicroserviceDiscountsRepository, `DatabaseDiscountsRepository```.

To jest błędne?

To zależy. Masz DDD?

  1. Jeśli korzystam z danych pobieranych spoza mojej władzy czyli serwisy innych autorów, firm i nie mam pojęcia gdzie je trzymają(nawet mnie to nie interesuje) to robić repozytorium czy DAO?

Jak masz DDD to możesz zrobić repozytorium, jak nie masz DDD, to możesz sobie taką klasę równie dobrze nazwać provider. A wszystko i tak zależy od tego jak tego używasz i co to zwraca.

1
Bogu napisał(a):

Jest jakaś aplikacja (z dobrymi praktykami) udostępniona na np. githubie, która jest na tyle rozwinięta, że można zobaczyć w akcji "dobre praktyki"? Czy trzeba wszystko mazać samemu od nowa? Bo tak czytając ten temat, to standardowo wychodzi kłótnia pomiędzy developerami o architekturę przez co informacje są rozmyte w kilkanaście postów :) Sam kiedyś znalazłem jakąś apkę w postaci internetowego sklepu muzycznego czy coś takiego ale nie wiem czy sposób implementacji odpowiada dobrym praktykom.

Całkiem sporo jest tutaj : https://www.microsoft.com/net/learn/architecture - zwałaszcza Microservices & Docker - porządna dawka wiedzy na temat nowoczenego użycia .NETa
Pewnie i tak nie pokrywa wszytskiego i znajdą się tacy którym się nie spodobaja niektóre rozwiązania, ale ogólnie jest to kawał dobrej roboty moim zdaniem.

0

Dzięki za odp.

somekind napisał(a):
Błękitny Orzeł napisał(a):
  1. A jeśli repozytorium zwraca i przyjmuje model domenowy to jest w porządku?

Jeśli masz DDD i przez model domenowy rozumiesz aggergation root, to tak.

Tak chodziło mi o to, że repozytorium operuje na Aggregate roocie.
A jeśli jeden agregat ma w sobie innego?

  1. Co jest złego w metodzie Update repozytorium? - Na prawdę nie wiem. Jak inaczej to nazwać gdy chcę zapisać zmiany w obiektu?

A co niby to Update tak konkretnie robi? Wysyła SQL update do bazy?
Bo generalnie do zmiany jakiejś wartości w obiekcie wystarczy użyć settera, a potem po prostu zatwierdzić transakcję w unit of work.

Metoda update robi w zależności od implementacji

  1. Zamienia obiekt w kolekcji w przypadku InMemory
  2. Wysyła POST do mikroserwisu za pomocą klienta wygenerowanego przez AutoResta - a co dalej się dzieje to nie wiem. Może sql.
  1. No skoro repozytorium to abstrakcja warstwy dostępu do danych to czemu nie może żyć bez DDD?

Bo zostało zdefiniowane jako część DDD.

Mam jeden interfejs IDiscountsRepository i implementacje w zależności od źródła danych InMemoryDiscountsRepository, MicroserviceDiscountsRepository, `DatabaseDiscountsRepository```.

To jest błędne?

To zależy. Masz DDD?
No nie wiem czy mam.

  1. Jeśli korzystam z danych pobieranych spoza mojej władzy czyli serwisy innych autorów, firm i nie mam pojęcia gdzie je trzymają(nawet mnie to nie interesuje) to robić repozytorium czy DAO?

Jak masz DDD to możesz zrobić repozytorium, jak nie masz DDD, to możesz sobie taką klasę równie dobrze nazwać provider. A wszystko i tak zależy od tego jak tego używasz i co to zwraca.

Co to znaczy mieć DDD? Konkretnie. Aggregate rooty, bounded contexty, prywatne settery?

Ogólnie projekt, na którym bazuje w tym temacie to wielka fasada do paru mikroserwisów(z których korzystam tylko ja :P (póki co!! - może kiedyś inny system będzie chciał)) oraz wystawiam WebAPI dla klineta Angularowego.
Powiedziano mi by zrobić to w DDD(więcej napiszę spoza kompa firmowego :P).


Tak jak pisałem moje repozytorium jako źródła danych używa mikroserwisu poprzez wygenerowanego klienta. Klient mimo DDD wystawia mi metody typu "Zmień SLA", "Zmień status", "Edycja", "Pobierz to", "Pobierz tamto", "Ustaw X", "Ustaw Y". No jest tego w groma. Tak wyglądało repozytorium, które stworzyłem. Było w postaci 1:1 względem metod.
Zmieniłem to na coś takiego, że

AggregateRoot 
{
	ArrayOfDomainEvents;
	
	Activate() 
	{
		this.Status = Active;
		this.ArrayOfDomainEvents.Add(new Activated()); // Uproszcozno
	}
	
	UpdateSla()
	{
		this.Sla = StaticDomainSlaService.CalculateSla(this.Created, ...);
		this.ArrayOfDomainEvents.Add(new SlaUpdated());
	}
}

A repozytorium wygląda teraz tak:

Repository 
{
	Update(AggregateRoot entity)
	{
		foreach domainEvent in entity.ArrayOfDomainEvents
			clientMethodToInvoke = this.Methods[domainEvent]; 
			clientMethodToInvoke(entity, domainEvent);
	}
	
	Methods 
	[
		[DomainEventType] = ConcreteMethodFromClientService
	]
}

Więc przykryłem metody klienta jedną metodą Update, które wykonywane są w zalezności o typu zdarzenia.
Wydaje mi się, że jest to takie trochę ubogie CQR gdzie manualnie(w kodzie) żągluje zdarzeniami.

Czy to jest dobre repozytorium, które ukrywa źródło danych?

0

Agregat nie może mieć w sobie innego. Łamiesz w tedy zasadę zmiany stanu tylko jednego agregatu.

Repo nie jest warstwą dostępu do danych tylko odwzorowania obiektu. Chodzi głownie o przetrzymywanie stanu DM.

Jaki masz problem żeby Update oraz zgaduje Save na kliencie wywoływać w metodzie Save() z repozytorium. Wtedy będziesz miał repozytorium typu trwałego bez UOW.

To co zrobiłeś wygląda mi bardziej na jakiś Port/Adapter z fasadą klienta.

Gdzie macie tą firmę, może mnie przyjmiecie do pracy co ?

0
Błękitny Orzeł napisał(a):

Tak chodziło mi o to, że repozytorium operuje na Aggregate roocie.
A jeśli jeden agregat ma w sobie innego?

To wtedy nie jest to DDD, więc nie jest aggregate rootem.

Metoda update robi w zależności od implementacji

  1. Zamienia obiekt w kolekcji w przypadku InMemory

Ale co zamienia? Jeśli jest w pamięci, to wystarczy przecież przypisać nową wartość i już jest zupdatowana.

  1. Wysyła POST do mikroserwisu za pomocą klienta wygenerowanego przez AutoResta - a co dalej się dzieje to nie wiem. Może sql.

Tak od razu? Bez UoW? Bez transakcji? To nie DDD.

Co to znaczy mieć DDD? Konkretnie. Aggregate rooty, bounded contexty, prywatne settery?

To znaczy pisać logikę biznesową aplikacji całkowicie zgodnie z tym wzorcem i używać go zgodnie z przeznaczeniem, a nie po prostu losowe klasy w projekcie używać nazwami z DDD.

Ogólnie projekt, na którym bazuje w tym temacie to wielka fasada do paru mikroserwisów(z których korzystam tylko ja :P (póki co!! - może kiedyś inny system będzie chciał)) oraz wystawiam WebAPI dla klineta Angularowego.
Powiedziano mi by zrobić to w DDD(więcej napiszę spoza kompa firmowego :P).

Jesteś nowy w tej pracy? Bo może to głupi dowcip dla świeżaka miał być...
No bo jeśli nie, to ktoś tam ewidentnie się z własnym przyrodzeniem na łby pozamieniał. DDD ma zastosowanie w projektach ze skomplikowaną logiką biznesową. Ty masz fasadę, więc nie masz żadnej logiki biznesowej, to jest idealny przykład miejsca, w którym DDD się nie uda nawet na siłę.

Więc przykryłem metody klienta jedną metodą Update, które wykonywane są w zalezności o typu zdarzenia.
Wydaje mi się, że jest to takie trochę ubogie CQR gdzie manualnie(w kodzie) żągluje zdarzeniami.

Czy to jest dobre repozytorium, które ukrywa źródło danych?

Nie, to nie jest żadne repozytorium. To jest po prostu zwykła fasada.

0
somekind napisał(a):
Błękitny Orzeł napisał(a):

Tak od razu? Bez UoW? Bez transakcji? To nie DDD.

Mylisz się, Repo Data Fabeic jest powszechnie urzywane z NonSql.

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