Implementacja najlepszych praktyk .NET Full Webdev na przykładzie własnej aplikacji pisanej po godzinach

Odpowiedz Nowy wątek
2019-02-09 16:51
4

Mój pierwszy post więc w ramach przywitania się z community od razu podzielę się wiedzą i swoją pracą ;)

Na moim koncie gh:
[email protected]://github.com/piotr-mamenas/performance-app

Udostępniłem aplikacje nad którą pracuję po godzinach od dwóch lat, apka składającą się w sumie z 10 assembly, w tym backendowego Serwisu RESTowego w Web Api 2 oraz aplikacji Postback (wspomaganej akcjami SPA w Ajax) napisanej w MVC 5. Służyła mi ona do pogłębiania wiedzy odnośnie architektury aplikacji. Generalnie jest to ekstrakt najlepszych praktyk sugerowanych przez guru .NET developmentu takich jak: Dino Esposito, Martin Fowler, Robert C. Martin czy Gary McLean Hall i wiele wielu innych których chętnie i fanatycznie czytam :)

Co zatem znajdziecie w środku?

  1. Najlepsze praktyki z zakresu Domain Driven Development z wykorzystanie jednego Bounded Context zawierającego kilkanaście agregatów Encji z pojedyńczym Entity Root per Agregat, odpowiadającym za tranzakcyność. Ponadto podział na Base Entities, Relationship Entities oraz ValueObjects. Nie-anemiczny model domeny (Encje domenowe zawierają logikę biznesową),
  2. Podział na tzn Domain Layer, Infrastructure Layer, Service Layer i Presentation Layer wraz z zastosowaniem reguł SOA
  3. Zastosowane najlepsze praktyki z zakresu SOLID. Patternów Go4 (np Facade, Singleton, Builder, Decorator, Strategy), DRY, KISS oraz innych.
  4. Na frontendzie kod Javascriptowy jest podzielony na oddzielne komponenty izolowane Revealing Module Pattern opartej na Closures. Jeśli chodzi o strukturę strony to głównie jest to Bootstrap 3 z mieszanką flexa.
  5. Dependency Injection z wykorzystaniem kontenera Ninject w celu wtrzykiwania Repozytoriów i Serwisów bezpośrednio do Konstruktorów Kontrolerów.
  6. Implementacja tranzakcyjności operacji poprzez patterny Unit of Work i Generic Repository z użyciem Entity Framework 6 podpiętej pod bazę relacyjną. Z podziałem na zewnętrzne konfiguracje oraz przeładowaniami konfiguracji dla Base Entities.
  7. Najlepszymi praktykami w serializacji danych z użyciem DTO, View Modeli oraz mapowania obiektów AutoMapperem.
  8. Authentykacje i Autoryzacje opartą na Identity Framework 2 oraz ciasteczkach.

Jeśli ktoś się uczy lub chciałby po prostu poszerzyć swoją wiedzę z zakresu dobrych praktyk to jest to miejsce w którym można podpatrzeć rozwiązania implementacyjne i mam nadzieję nauczyć się czegoś nowego ;)

Domain Driven Design ;) - Aventus 2019-02-09 18:11

Pozostało 580 znaków

2019-02-09 17:20
4

Zaraz przyjdzie @somekind i Ci powie że Generic Repository to anti-pattern.

Pozostało 580 znaków

2019-02-09 17:40
1
kzkzg napisał(a):

Zaraz przyjdzie @somekind i Ci powie że Generic Repository to anti-pattern.

I częściowo będzie miał racje, ale to zależy też od konkretnego zastosowania, jeśli aplikacja głównie wykonuję operacje CRUD'owe w REST to generic repository może się okazać dobry patternem bo kod jest bardzo powtarzalny i użycie generic repository ułatwia development. Jeśli jest to jednak aplikacja ze sporą ilością customowej logiki to powinno się skłaniać bardziej w stronę zwykłego repository (Większość case'ów).

Generalnie generyczność sprawia że wystawione są metody którę potencjalnie developer mógłby użyć do wylania logiki fetchującej dane poza repozytoria do kontrolerów a to zaszarza kod na długą metę i generuje duplikacje. W małych zespołach które są dobrze zdyscyplinowane nie stanowi to problemu przy odpowiedniej komunikacji ale w duży zespołach nie da się upilnować żeby jakiś junior nie przepchnął pull requesta z dziwnym kodem.

Pozostało 580 znaków

2019-02-09 18:22
0

Jeśli ktoś robi repozytorium do CRUDa, to znaczy, że w ogóle nie rozumie po co istnieją repozytoria.
A opakowanie contextu EF we własne genetyczne repozytorium to zwykły WTF.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
Pokaż pozostałe 8 komentarzy
Czyli jedna wielka delegacja przez dwie warstwy i jakoś mnie to nie przekonuje. Jak bys to rozwiązał? - Desu 2019-02-12 17:56
@Desu, tak, raczej mam serwisy, które operują na context. W przypadku samego odczytu również. Przy czym te "serwisy" mam mądrzej nazwane (np. CośtamReader, CośtamProvider albo CośtamCommand). Nie mam po prostu niczego nie wnoszącej warstwy "repozytoriów", które de facto repozytoriami nie są, bo to tylko DAO będące wrapperami na ORM. - somekind 2019-02-13 01:39
@somekind: dzięki. A jak walczysz z argumentem w stylu: "dzięki Repozytoriom możemy wymienić ORM'a!"? - Desu 2019-02-13 12:59
Pytam, czy któryś ze zwolenników kiedykolwiek coś takiego zrobił w jakimkolwiek projekcie. - somekind 2019-02-13 14:14
Fair enough :D - Desu 2019-02-13 14:14

Pozostało 580 znaków

2019-02-09 20:33
0

Wszystko zalezy od sytuacji. EF to konkretna technologia i uzywajac jej wszedzie bezposrednio wiazemy z nia nasz kod. A juz na pewno nie powinnismy wiazac z nim logiki domeny. Raz jeszcze podkreslam bo to wazne- zalezy od sytuacji. Gdzies takie uzaleznianie bedzie akceptowalne, a gdzies nie. Tak naprawde dyskusja dotyczaca wrapowania ORMa fajnie obnaza kwestie tego ze nie ma uniwersalnych dogmatow (o czym wspomnialem w komentarzu wyzej). Nagle okazje sie ze zasada programowania do interfejsow jednak nie zawsze jest stosowana. Bardzo dobrze! Jesli nie ma takiej potrzeby, to sie tego nie robi. Podobnie z wrapowaniem ORMow. Inna osoba/zespol natomiast moze wolec wyabstrahowac technologie perzystencji/ORM- rowniez nie ma problemu.

Chociaz w tym przypadku, kiedy czytam DDD i CRUD to jakos jedno nie za bardzo z drugim pasuje. Albo mamy jakas konkretna, chociaz minimalnie zlozona domene albo cos do czego wystarczy zwykly CRUD. Jesli to drugie, to gdzie tu miejsce na repozytoria w sensie DDD? Swoja droga w tym linku wyraznie jest napisane A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection, wiec jesli uznac Martina Fowler'a za jakis autorytet (chyba mozna sie zgodzic ze tak wlasnie jest, prawda?) to wychodzi na to ze abstrahowanie EF (mapping layer) jest czyms normalnym.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 1x, ostatnio: Aventus, 2019-02-09 20:36

Pozostało 580 znaków

2019-02-09 21:08
1

Ale właśnie w podanym cytacie jest ładnie wyjaśnione czym jest repozytorium i co przechowuje. Zdecydowanie nie chodzi o przechowywanie encji bazodanowych, tylko o obiekty domenowe. Z wszystkimi ich zachowaniami i relacjami. Z tego co widzę, tutaj "repo" po pierwsze nie przechowuje żadnych obiektów, a tylko je zwraca, po drugie, zwraca właśnie encje, a nie obiekty domenowe.

Z tego powodu jest tylko wrapperem, głupią przelotką. I poza zasłonięciem odwołania do EF niczego nie oferuje, więc w gruncie rzeczy jest zbędne w tym przypadku i dodaje niepotrzebną warstwę, którą trzeba przeszukać w kodzie, gdy szuka się jak jest budowane wyciąganie encji.

Edit: Przy czym w tym kontekście mam na myśli, że "Encja" to encja w sensie bazy danych, nie obiektu domenowego jakim jest w DDD.

edytowany 1x, ostatnio: Klojtex, 2019-02-09 21:19
Ale encja to jest przecież obiekt domenowy w DDD. - somekind 2019-02-09 21:16
Ale dlategoż przeca napisałem "encji bazodanowych", teraz widzę, że zjadłem to w dalszej części. Zaraz dopiszę. - Klojtex 2019-02-09 21:17

Pozostało 580 znaków

2019-02-09 21:16
0

@Klojtex: zaznaczam że nie pisałem o generycznym repo tak jak w tym przypadku. Masz jak najbardziej rację. Jedno tylko sprostowanie- z dzisiejszymi frameworkami rzadko kiedy istnieje potrzeba oddzielenia encji z baz danych od modeli domenowych. W większości przypadków to właśnie model domenowy będzie zapisywany jako encje. Zazwyczaj nie ma potrzeby na klasy pośrednie (encje) ponieważ modele domenowe mogą być używane przez ORM przy zachowaniu ignorancji perzystencji.

EDIT: przepraszam, użyłem złego taga.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 4x, ostatnio: Aventus, 2019-02-09 21:19

Pozostało 580 znaków

2019-02-09 21:20
1
Aventus napisał(a):

Zazwyczaj nie ma potrzeby na klasy pośrednie (encje) ponieważ modele domenowe mogą być używane przez ORM przy zachowaniu ignorancji perzystencji.

Pod warunkiem, że użyje się ORMa, który to umożliwia. Czyli nie EF.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
Pokaż pozostałe 7 komentarzy
HEHE, przestań te bzdury wypisywać :D - Hosap 2019-02-10 10:55
Tak wiem, bany dostajesz za niewinnosc... Nie dyskutuje z Toba wiecej. - Aventus 2019-02-10 10:56
Randomów nie troluje ty też, nie jesteś bez winy. - Hosap 2019-02-10 10:58
A tak na serio, to niby co to za persistence ignorance jak zawsze trzeba albo to oblepić atrybutami, dodać virtuale, albo jakieś magiczne identyfikatory, lub klasę mapującą, To już lepiej napisać klasę pośrednią przynajmniej syf się nie robi. - Hosap 2019-02-10 11:02
Poza tym pomógł byś koledze w temacie chyba nie brakuje ci wiedzy z zakresu DDD, SOA, dobrych praktyk. - Hosap 2019-02-10 11:11

Pozostało 580 znaków

2019-02-10 06:48
0

Rozumiem, że się bardzo starasz i to co powiem może zaboleć. No więc.

Najlepsze praktyki z zakresu Domain Driven Development z wykorzystanie jednego Bounded Context zawierającego kilkanaście agregatów Encji z pojedyńczym Entity Root per Agregat, odpowiadającym za tranzakcyność. Ponadto podział na Base Entities, Relationship Entities oraz ValueObjects. Nie-anemiczny model domeny (Encje domenowe zawierają logikę biznesową),

Nie odróżniasz Encji od ValueObject ale nie martw się to powszechne. Ogólnie słaby model wywalasz wszędzie porpertisy i enumy, zamiast modelować język. Nie używasz identityField, zamiast tego agregujesz inne agregaty :P.

W niektórych sytuacjach obarczasz agregat odpowiedzialnością, która go nie dotyczy, zamiast spleść to w fabryce.

Mógłbyś spokojnie wydzielić 3 konteksty i 3 dziedziny w tym jedną główną. Pewnie nigdy nie narysowałeś ani jednej mapy kontekstów, zgadza się.?

Podział na tzn Domain Layer, Infrastructure Layer, Service Layer i Presentation Layer wraz z zastosowaniem reguł SOA

A jeśli to SOA z DDD, to dlaczego warstwa serwisowa-prezentacyjna widzi wszystkie warstwy.? Moim zdanie takie coś nazywa się lazanią.

Udostępniłem aplikacje nad którą pracuję po godzinach od dwóch lat,

Nie ściemniaj, dobra. :P

Szczerze to nie chcę mi się wszystkiego wytykać. Poza tym mam swoją robotę.

Pozostało 580 znaków

2019-02-10 14:11
0

@RoboCat @somekind @Klojtex

  1. Jeśli chodzi o repozytoria to jest to często źle rozumiany koncept, repozytorium jest implementacją idei Inversion of Control w SOLID a zatem:
    "High-level modules should not depend on low-level modules. Both should depend on abstractions."

Taką formą abstrakcji od Entity Framework będącego "High-level module" jest repozytorium. Dzięki użyciu repozytorium jesteśmy w stanie odizolować wykorzystanie metod ORM'a od faktycznej logiki biznesowej i nawet wymienić ORM'a jak zmienią się wymagania co nie byłoby wykonalne gdybyśmy wszędzie mieli kwerendy linq'owe bijące do czystego context'u. Druga sprawa to czysty porządek. W tej architekturze repozytoria odpowiadają za enkapsulacje logiki chwytającej dane, serwisy za wykonywanie logiki biznesowej a kontrolery tylko wystawiają serie operacji. O wiele przyjemniej czyta się w kontrolerach coś takiego:

  var partnerPayments = paymentRepository.getByPartnerId(partnerId);
  var paymentSettlementResult = paymentService.settlePayments(partnerPayments);
  if (paymentSettlementResult.IsValid) {
    etc ...
  }

aniżeli setki opatrzonych grubymi komentarzami kwerend linqowych lub co gorsza bicia do procedur na bazie. W ten sposób unikamy też dużej ilości duplikacji kodu bo po nazwach łatwo wnioskuje się że coś już istnieje i nie trzeba tego jeszcze raz pisać ;)

W tym kontekście, dobrze się czyta tych panów:
https://programmingwithmosh.com/tag/repository-pattern/
https://lostechies.com/jimmyb[...]ices-in-domain-driven-design/

Inna kwestia to zastosowanie izolacji tranzakcyjnej na poziomie aplikacyjnym przez użycie Unit of Work pattern, co pozwala zapewnić że wszystkie operacje wykonane w jednym requescie albo wszystkie sie powiodą albo wszystkie trafi szlag a nie że tranzakcje w połowie będą zapisywane do bazy bo developer przy użyciu contextu może sobie dowolnie zapisywać zmiany kilkukrotnie w jednym requescie. Repozytorium umożliwia wdrożenie UoW.

Nie jest prawdą że repozytorium to część DDD, repozytorium może być tak samo używane z ideą Event Sourcing'u w których modele domenowe jako takie mogą nawet nie istnieć, a źródłem danych są wyłącznie zdarzenia (fajnie współpracuje to z BDD). Są też rozwiązania które łączą wszystko razem z użyciem CQRS'a naprzykład. Co się tyczy konkretnej implementacji to zależy od Use Case'a, w niektórych małych aplikacjach bawienie się w zaawansowaną architekturę nie ma sensu i dodaje tylko complexity. W przypadku zmiany samego Generic Repository na proste Repository to jest to na liście poprawek od jakiegoś czasu w tym projekcie ;)

@Aventus @RoboCat @somekind @Klojtex

  1. Encje i modele Domenowe to jedno i to samo, w przypadku Entity Framework 6 z użyciem Code First reprezentowane pojedyńczymi klasami POCO oraz opcjonalną klasą konfiguracyjną pozwalającą nam zkonkretyzować jak ORM ma się zachowywać w danej situacji.

Co się tyczy EF, to jest to jedna z możliwych implementacji, równie dobrze można użyć innego ORM takiego jak nHibernate albo klasyczne relacje zdesignowac pod document model i chwycić po np Mongo albo RavenDB. Nie jest również prawdą że EF nie obsługuje ignorancji persystencji:
Lista providerów EF6: https://docs.microsoft.com/en-us/ef/ef6/fundamentals/providers/

Warto podkreślić że EF jest jednak ORM'em skupionym głównie na bazach relacyjnych, tak samo jak nikt raczej nie wymaga żeby MongoDriver nagle zaczął dobijać się do baz relacyjnych, tak nie oczekujmy że EF promowany jako Object Relational Mapper nagle zacznie bezkrytycznie współpracować tak z waszymi plikami płaskimi jak i bazami noSQLowymi.

Imho singleton nie jest anti-patternem, jest to natomiast pattern który jest dosyć nadużywany i przez to często mówi się o nim jako anti-patternie. W miejscach gdzie nie możemy pozwolić na to żeby w aplikacji istniały dwie różne instancje tej samej klasy lub chcemy zablokować możliwość równoczesnego dostępu do property singleton sprawdza się świetnie. Mówię tu chociażby o konfiguracji aplikacji czy dostępie do security. W praktyce wszystko zależy od case'a. Nawet Service Location może być wykorzystany w wyjątkowych sytuacjach poprawnie bez odnoszenia się do niego jako Anti-patterna.

@Hosap
Strasznie dużo nieuzasadnionej wyższości i arogancji w tym poście ale mimo wszystko postaram się odnieść (choć już widzę po komentarzach innych że mam do czynienia z trollem).

Nie wiem co rozumiesz przez nieodróżnianie Entity od ValueObject, ValueObject to wartość wbudowana wewnątrz entity czyli zamiast oddzielać coś do innej tabeli, przechowujemy jako compound type na innych tabelach zduplikowany. Najprostszym przykładem może być typ Money któremu samemu w sobie tabela się nie należy ale możemy przy jej pomocy zmodelować operacje na gotówce (monety etc) i fajnie żeby bez dodatkowej tabeli ta logika móc wykorzystać w poszczególnych encjach. I w ten właśnie sposób Value Object użyty jest w tej konkretnej implementacji.

Pozostałe rzeczy które powiedziałeś nie mają najmniejszego sensu. Mylisz też pojęcia, Monolith często nazywany lazanią jest typem architektury, tak jak typem architektury są mikroserwisy (I tak to repo to monolith). SOA i DDD natomiast to paradygmaty które można użyć w konkretnych typach architektury, DDD można użyć tak w Onion'ie jak i Mikroserwisach. Równie dobrze można zastąpić DDD, Event Sourcingiem albo użyć Event Sourcing w połączeniu z CQRS'em i DDD. Nie mają zbyt wiele wspólnego z samym typem architektury.

Nie ściemniaj, dobra. :P

Nie rozumiem? masz historię commitów na repo? Projekt zaczynałem dwa lata temu w celu dogłębnego zrozumienia rzeczy z którymi spotykałem się na co dzień na projektach w pracy i nie zawsze do końca rozumiałem zależności.

@Aventus @RoboCat @somekind @Klojtex @Hosap
Część implementacji może nie być optymalna bo dawno nie były revisitowane tak jak w przypadku braku podziału na Bounded Contexts czy użyciu Generic Repository gdzie klasyczne Repo starczyłoby. To tylko zbiór mający zobrazować jak wszystkie zasady wyglądają wspólnie i był moim polem eksperymentalnym w wielu wypadkach, nie wszystko jest perfect, fair enough, zapraszam do submittowania pull requestów albo podzielenia się własną pracą. Ja mam własną listę poprawek i na bieżąco ulepszam tą architekturę żeby ludzie mieli wgląd jak wygląda to wszystko razem i samemu też na bieżąco się czegoś uczyć.

Nie wdawaj sie w dyskusje z uzytkownikiem Hosap. To troll ktory juz wczesniej byl kilkakrotnie banowany. Ignoruje wszelkie racjonalne argumenty i tworzy swoje pokrecone teorie. Takich ludzi trzeba po prostu ignorowac. - Aventus 2019-02-10 16:32
Nie masz pojęcia, do czego służy Repozytorium i Unit Of Work. Już mniejsza o mnie, ale jak chcesz Somkins'owi, tłumaczyć czym jest repo to lepiej na grzyby pójdź do lasu. - Hosap 2019-02-12 13:03
A ten art powstał, po tym jak bojkotowałem tego łysola na YouTube i jak widać niczego się nie nauczył, bo dalej głupoty pisze. https://programmingwithmosh.com/tag/repository-pattern/ - Hosap 2019-02-12 13:05
"4 Common Mistakes with the Repository Pattern" sry, o ten art mi chodziło. - Hosap 2019-02-12 13:07
A to stąd wziąłeś te Repository i Unit Of Work, hahah.... Nie mogę..... https://www.youtube.com/watch?v=rtXpYpZdOzM&t=1334s - Hosap 2019-02-12 13:20
A jak ci się wydaje jak te "Unit Of Work" ma się do Open Closed Principle.? Geniuszu... Co mnie Trolem nazywa, który pisze głupoty... To ty piszesz głupoty, jedna z nich to ten projekt. - Hosap 2019-02-12 13:23

Pozostało 580 znaków

2019-02-10 16:39
1

Równie dobrze można zastąpić DDD, Event Sourcingiem(...)

To akurat wprowadza w blad. DDD nie jest wzorcem ani zadnym konkretnym rodzajem architektury i nie powinien byc przedstawiany w opozycji do ES czy CQRS.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Proszę mnie bardzo nie łapać za słówka ;) W dalszej części posta w jasny sposób podkreślam że są to oddzielne koncepty które można nawet używać wszystkie naraz wspólnie jeśli uzasadnia to business case. A odnośnie Hosap to zauważyłem haha :D - roch.mamenas 2019-02-10 17:09
Nie lapie ;) Po prostu stwierdzilem ze warto napisac w razie gdyby ktos przeoczyl i zle odebral Twoje slowa. - Aventus 2019-02-10 17:11
Ok Ok ;) Nie czepiam się już ;p - roch.mamenas 2019-02-10 17:13
czyli można czytać i czerpać legit wiedzę? - Visual Code 2019-02-10 17:49
A odnośnie Hosap to zauważyłem haha :D Och naprawdę. - Hosap 2019-02-10 19:54

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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

Robot: MJ12bot