Warstwy Interfejsu, Programowanie Asynchroniczne, Kilka Pytań

0

Jest kilka zagadnień które nie dają mi spokoju ponieważ co czyjaś publikacja widzę inne podejście.

Chciałbym przedstawić przykładowy kod a potem zadać kilka pytań.

Jest to Model domenowy. ORM to EF. Wszystkie Adnotacje atrybutów odnośnie formularza przechowywane są ViewModel

    public class Person
    {
        // primitive properties
        public int Id { get; set; }
        [MaxLength(150)]
        public string Name { get; set; }
     }

Interface

    public interface IPersonManager
    {
        /// <summary>
        /// Edits existing person
        /// </summary>
        Task EditPerson(Person person);
    }
 

Klasa PersonManager Jest to repo Które dziedziczy interface IPersonManager.

        public async Task EditPersonAsync(Person person)
        {
            _dbContext.Entry(person).State = EntityState.Modified;
            await _dbContext.SaveChangesAsync();
        }

Klasa PersonComponent Odpowiada za logikę oraz Mapowanie modelu domenowego na ViewModel. Wiem że są dostępne biblioteki Automaperów ale znacznie spowalniają one program.

        private readonly IPersonManager _personManager;

        public PersonComponent(IPersonManager personManager)
        {
            _personManager = personManager;
        }

        private Person MapPersonViewModelToModel(PersonViewModel personViewModel)
        {
            Person person = new Person();

            person.Id = personViewModel.Id;
            person.Name= personViewModel.Name;
            person.Phone = personViewModel.Phone;

            return person;
        }

        public async Task EditPersonAsync(PersonViewModel personViewModel)
        {
            Person person = MapPersonViewModelToModel(personViewModel);

            await _personManager.EditPersonAsync(person);
        }

Kontroler

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Edit([Bind(Include = "Id,Name")] PersonViewModel personViewModel)
        {
            if (ModelState.IsValid)
            {
                await _personComponent.EditPersonAsync(personViewModel);
                return RedirectToAction("Index");
            }
            return View(personViewModel);
        }

Co myślicie o podejściu "Anemic Domain Model" I w jaki sposób określić wtedy właściwości atrybutów encji.

Czy moje podejście do programowania asynchronicznego jest ok? Czy np. Kontroler powinien być jedynie wrapem synchronicznym który wykonuje metodę asynchroniczną. Szczerze to nie do końca potrafię sobie wyobrazić co się dokładnie dzieje w kodzie i wątkami w czasie pracy programu.

Czy moje podejście rozłożenia kodu na warstwy jest ok ?

Czy Repo z EF to dobry pomysł jeśli jest to swoistego rodzaju abstrakcja na abstrakcji ?

Jaki Automaper możecie polecić?

1
WebJarek napisał(a):

Czy Repo z EF to dobry pomysł jeśli jest to swoistego rodzaju abstrakcja na abstrakcji ?

Jaki Automaper możecie polecić?

Bez sensu jest stosowanie wzorca repozytorium, to jeden z najbardziej nadużywanych wzorców fajny opis masz tutaj - http://commitandrun.pl/2016/05/11/Repozytorium_najbardziej_niepotrzebny_wzorzec_projektowy/ . Nie bardzo widzę sens zastosowanie klasy ViewModel'u jeżeli nie różni się ona niczym od klasy Person. Źle jest nazywać klasy cośtamManager, takie klasy szybko zmieniają się w przechowalnie wszystkiego. Jaki Automaper ? AutoMapper :D

3
mariano901229 napisał(a):

(...) fajny opis masz tutaj - http://commitandrun.pl/2016/05/11/Repozytorium_najbardziej_niepotrzebny_wzorzec_projektowy/ . Nie bardzo widzę sens zastosowanie klasy ViewModel'u jeżeli nie różni się ona niczym od klasy Person.

Na tym samym blogu w którymś z postów jest również o tym żeby obiekty z warstwy biznesowej nie wyciekały do widoków i żeby zawsze stosować VM i nie łamać zależności między warstwami, nawet jeśli nic nie wnoszą, także masz trochę wybiórcze podejście :P

WebJarek napisał(a):

Wiem że są dostępne biblioteki Automaperów ale znacznie spowalniają one program.

Nie do końca jest to prawdą, koszt związany z użyciem dobrego mapera zwykle ponosi się raz w momencie ustawienia mapowania między klasami, maper w tym czasie emituje kod co trochę trwa, ale później każde wywołanie mapowania jest porównywalnie szybkie do tego co by się ręcznie napisało.

WebJarek napisał(a):

Co myślicie o podejściu "Anemic Domain Model" I w jaki sposób określić wtedy właściwości atrybutów encji.

Zarówno w podejściu anemic jak i rich domain model w DDD, model domenowy ma być Persistence Ignorance, więc zapomnij o dodawaniu jakichkolwiek atrybutów do encji. Właściwości określa się wtedy bezpośrednio w warstwie dostępu do danych, jaką jest w Twoim przypadku EF. Co np robi się tak:

public class DomainModelFacade : DbContext 
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
          //setting a property as the key
           modelBuilder.Entity<Project>().HasKey(x => x.ProjectId);
    }
}
WebJarek napisał(a):

Czy Repo z EF to dobry pomysł jeśli jest to swoistego rodzaju abstrakcja na abstrakcji ?

Jeśli idziesz w DDD, to tak, jeśli nie wiesz co to, to Twoje repozytorium to zapewne nie repozytorium tylko DAO, i najprawdopodobniej go nie potrzebujesz, aczkolwiek wszystko zależy od kontekstu.

WebJarek napisał(a):

Czy moje podejście rozłożenia kodu na warstwy jest ok ?

w miarę tak, z tego co można się domyśleć, chociaż też to zależy co chcesz osiągnąć, obecnie jesteś gdzieś po środku między CRUDEM z Data-Driven Designa a LOBem z Domain-Driven Design

w kontrolerze masz logikę widoku
w PersonComponent masz logikę aplikacji (co zwykle się nazywa serwisami - mniej ogólne określenie niż komponent)
masz VM do transportu danych między warstwą aplikacji a widokiem
PersonManager należy do warstwy dostępu danych, który jest albo Repozytorium albo DAO, ciężko powiedzieć

nie wiadomo tylko gdzie masz logikę biznesową, w przypadku DDD byłby to oddzielne serwisy i encje, a jak nie idziesz w DDD i jest jej nie dużo to pewnie się znajdzie w PersonComponent ;)

0

Jesteś wielki, pięknie to opisałeś i dobrze wyczułeś z czym mam problem. Pewnie trochę przy tym siedziałeś, więc dzięki. Możesz polecić jakieś dobre książki o DDD i TDD. Jakie wzorce projektowe mogą mi się jeszcze przydać przy ASP.NET.? Może natrafiłeś na jakąś dobrą publikacje na temat DDD najbardziej do mnie trafia gdy ktoś omawia kod niż suche regułki.

2

Przytoczony wcześniej blog http://commitandrun.pl, jest cały warty przejrzenia bo się rozprawia z wieloma mitami/błędami w aplikacjach stojących na mvc + orm.

Jeśli chodzi o książki, to jest znakomita książka Dino traktująca przeglądowa o architekturze aplikacji biznesowych, więc nie tylko jest o DDD, ale także można poczytać sobie chociażby o CQRS:
Microsoft .NET - Architecting Applications for the Enterprise (2nd Edition)
czyta się ją bardzo przyjemnie, niestety jest bardzo krótka jak na ilość tematów które porusza, przez co zdecydowanie ich nie wyczerpuje, jak dla mnie pozycja obowiązkowa dla wszystkich.

A jeśli chodzi o czyste DDD, to są dwie kanoniczne pozycje: niebieska i czerwona książka, ale to już jest raczej ciężka lektura:
Domain-Driven Design: Tackling Complexity in the Heart of Software (niebieska)
Implementing Domain-Driven Design (czerwona)
obie dostępne po polsku wydane przez helion, tylko nie wiedzieć czemu zamienili kolorystykę (żartownisie)

Co do TDD to ciężko mi coś polecić do czytania, bo to co pisze największy orędownik TDD - Uncle Bob średnio do mnie przemawia, a lektura klasycznej książki o TDD Becka dopiero przede mną, zresztą i tak uważam że to trzeba przede wszystkim praktykować, bo z czytania o tym się nie wiele wyniesie ;)

Co do wzorców to bardzo ciężkie pytanie, bo wzorców i podejść jest mnóstwo, i nie istnieją jedyne słuszne które zawsze się sprawdzą. Dobrze mieć szerokie rozeznanie w tym temacie, tak by dobierać rozwiązanie do problemu, a nie próbować wszystko rozwiązać za pomocą jednego młotka. Stąd właśnie tak wiele różnych podejść w publikacjach, o których wspomniałeś na samym początku - pewnie starają się rozwiązać różne problemy w głowie autora, aczkolwiek bez wyspecyfikowania jawnie jakie, więc dobrze umieć to rozpoznać i ocenić czy to jest także potrzebne w naszym przypadku.

2
WebJarek napisał(a):

Jest to Model domenowy. ORM to EF. Wszystkie Adnotacje atrybutów odnośnie formularza przechowywane są ViewModel

    public class Person
    {
        // primitive properties
        public int Id { get; set; }
        [MaxLength(150)]
        public string Name { get; set; }
     }

Ale to nie jest model domenowy, tu nie ma żadnej logiki...

Klasa PersonManager Jest to repo Które dziedziczy interface IPersonManager.

        public async Task EditPersonAsync(Person person)
        {
            _dbContext.Entry(person).State = EntityState.Modified;
            await _dbContext.SaveChangesAsync();
        }

W jakim celu mieszasz logikę biznesową ze szczegółami infrastrukturalnymi takimi jak Task? Jak wygląda testowanie tego?

Klasa PersonComponent Odpowiada za logikę oraz Mapowanie modelu domenowego na ViewModel. Wiem że są dostępne biblioteki Automaperów ale znacznie spowalniają one program.

Że niby CRUD, który renderuje HTMLa, przesyła dane przez sieć, a potem woła ORMa do operacji na bazie po drodze znowu coś przesyłając przez TCP/IP będzie "znacznie spowolniony" przepisywaniem właściwości między obiektami?
Jak to zmierzyłeś?

Co myślicie o podejściu "Anemic Domain Model" I w jaki sposób określić wtedy właściwości atrybutów encji.

Co kto lubi, tylko jak się stosuje ADM to wypadałoby nie nazywać swoich DAO repozytoriami.

Czy Repo z EF to dobry pomysł

Nie - ani jedno, ani drugie to nie jest dobry pomysł. Ale jeśli już masz EF, to nie utrudniaj sobie życie pisząc jakieś pseudorepozytoria, bo to tylko strata czasu.

mariano901229 napisał(a):

Nie bardzo widzę sens zastosowanie klasy ViewModel'u jeżeli nie różni się ona niczym od klasy Person.

Nigdy nie jest tak, żeby się nie różniła - no chyba, że ktoś źle zaprojektował widok albo struktury danych.

neves napisał(a):

Na tym samym blogu w którymś z postów jest również o tym żeby obiekty z warstwy biznesowej nie wyciekały do widoków i żeby zawsze stosować VM i nie łamać zależności między warstwami

O ile mnie pamięć nie myli, to nawet w tym samym. :)

maper w tym czasie emituje kod co trochę trwa

Emisję kodu miał nieco leciwy EmitMapper oraz Automapper od wersji 4, czyli całkiem niedawno. Większość mapowaczy działa jednak na refleksji.

model domenowy ma być Persistence Ignorance, więc zapomnij o dodawaniu jakichkolwiek atrybutów do encji.

No, ale to należy zapomnieć także o użyciu EF, bo z tym ORMem osiągnięcie persistence ignorance nie jest możliwe.

neves napisał(a):

Jeśli chodzi o książki, to jest znakomita książka Dino traktująca przeglądowa o architekturze aplikacji biznesowych, więc nie tylko jest o DDD, ale także można poczytać sobie chociażby o CQRS:

Przy czym Dino także promuje używanie persistence modelu jako view modelu czyli tzw. "encja na twarz i pchasz". Dlatego sugeruję zostać przy commitandrun.

0

Ale to nie jest model domenowy, tu nie ma żadnej logiki...

Ale przecież domena to dziedzina aplikacji a model to część logiki z nią związana.
Więc model domenowy to ta część która określa dziedzinę która będzie wykorzystywana przez logikę lub inaczej tą część dziedziny która będzie modelowana przez aplikację :P więc ta klasa jak najbardziej do tego opisu pasuje.
Z twojego zdania można wywnioskować że ADM to wyrzucenie całkowicie logiki z modelu a tak nie jest ta logika jest po prostu wyrzucona jak by za płot. Czyli odseparowana od danych a raczej kodu który jest za nią odpowiedzialny.

Co kto lubi, tylko jak się stosuje ADM to wypadałoby nie nazywać swoich DAO repozytoriami.

Co ADM ma do tego czy w aplikacji zostanie zastosowane DAO, DAL, DOT czy repo ?

W jakim celu mieszasz logikę biznesową ze szczegółami infrastrukturalnymi takimi jak Task? Jak wygląda testowanie tego?

Właśnie nie potrafię sobie wyobrazić jak tą asynchroniczność odwzorować w kodzie kiedy jest on podzielony na warstwy. Jeśli był byś tak miły i dał mi jakiś przykład był bym bardzo wdzięczny.

Że niby CRUD, który renderuje HTMLa, przesyła dane przez sieć, a potem woła ORMa do operacji na bazie po drodze znowu coś przesyłając przez TCP/IP będzie "znacznie spowolniony" przepisywaniem właściwości między obiektami?
Jak to zmierzyłeś?

Przyjąłem konwencje Model=>ViewModel. Gdzie ViewModel to klasa modelu która jest odpowiedzialna za prezentowanie danych dla View. W teorii każda nowa klasa w kodzie spowalnia aplikacje :P

A ten kod to po prostu przykład. Nieważne czy to CRUD czy jakaś zaawansowana logika. Chodzi o nabranie dobrych nawyków związanych z projektowaniem tej logiki Jakim jest Metodyka która wiąże się z wzorcami, konwencją - nazewnictwem.

1
somekind napisał(a):
neves napisał(a):

model domenowy ma być Persistence Ignorance, więc zapomnij o dodawaniu jakichkolwiek atrybutów do encji.

No, ale to należy zapomnieć także o użyciu EF, bo z tym ORMem osiągnięcie persistence ignorance nie jest możliwe.

Podręcznikowego persistence ignorance może i nie, ale w moim odczuciu "wystarczającego" by móc go rozważać jako kandydata do tej roli.

somekind napisał(a):
neves napisał(a):

Jeśli chodzi o książki, to jest znakomita książka Dino traktująca przeglądowa o architekturze aplikacji biznesowych, więc nie tylko jest o DDD, ale także można poczytać sobie chociażby o CQRS:

Przy czym Dino także promuje używanie persistence modelu jako view modelu czyli tzw. "encja na twarz i pchasz". Dlatego sugeruję zostać przy commitandrun.

Z tym że Dino promuje to bym się kłócił, bo wyraźnie podkreśla które rozwiązanie jest w pełni poprawne, natomiast nie da się ukryć że dopuszcza on również encję na twarz i pchasz - ja też uważam że zdarzają się projekty w których takie podejście ma rację bytu i nie ma co na siłę wprowadzać zbędnej złożoności przez niepotrzebną abstrakcję, najważniejsze jest to by dokonywać świadomego wyboru, i wiedzieć jakie konsekwencje niesie podjęcie danej decyzji.

1
WebJarek napisał(a):

Ale przecież domena to dziedzina aplikacji a model to część logiki z nią związana.
Więc model domenowy to ta część która określa dziedzinę która będzie wykorzystywana przez logikę lub inaczej tą część dziedziny która będzie modelowana przez aplikację :P więc ta klasa jak najbardziej do tego opisu pasuje.

Model domeny (domain model) to taki wzorzec projektowy, w którym modeluje się dziedzinę poprzez obiekty zawierające dane i operacje na nich: https://martinfowler.com/eaaCatalog/domainModel.html
(Właściwie nie wiem, czemu jest to uznane za wzorzec, skoro łączenie danych i operacji to jedna z podstaw OOP.)

Z twojego zdania można wywnioskować że ADM to wyrzucenie całkowicie logiki z modelu a tak nie jest ta logika jest po prostu wyrzucona jak by za płot. Czyli odseparowana od danych a raczej kodu który jest za nią odpowiedzialny.

Twoja klasa nie zawiera logiki, więc nie jest modelem domenowym. Nie jest też nawet ADM lecz jedynie modelem składowania (Persistence Model), bo przecież to klasa używana przez ORMa do mapowania na tabelę.
http://blog.sapiensworks.com/post/2012/04/07/Just-Stop-It!-The-Domain-Model-Is-Not-The-Persistence-Model.aspx

Co ADM ma do tego czy w aplikacji zostanie zastosowane DAO, DAL, DOT czy repo ?

To, że repozytorium to wzorzec projektowy będący częscią podejścia DDD. ADM jest zaprzeczeniem DDD, więc w jego przypadku z definicji nie możesz mieć repozytorium: http://commitandrun.pl/2016/05/11/Repozytorium_najbardziej_niepotrzebny_wzorzec_projektowy/

Właśnie nie potrafię sobie wyobrazić jak tą asynchroniczność odwzorować w kodzie kiedy jest on podzielony na warstwy. Jeśli był byś tak miły i dał mi jakiś przykład był bym bardzo wdzięczny.

Ja bym po prostu oddzielił biznes od infrastruktury. Kontroler czy serwis WebAPI/WCF moga być asynchroniczne, ale już logika biznesowa niech zwraca biznesowe wyniki.

A ten kod to po prostu przykład. Nieważne czy to CRUD czy jakaś zaawansowana logika. Chodzi o nabranie dobrych nawyków związanych z projektowaniem tej logiki Jakim jest Metodyka która wiąże się z wzorcami, konwencją - nazewnictwem.

Robienie ręcznie tego, co może zrobić automat to jest bardzo zły nawyk.

neves napisał(a):

Podręcznikowego persistence ignorance może i nie, ale w moim odczuciu "wystarczającego" by móc go rozważać jako kandydata do tej roli.

Albo spełniamy PI albo nie, nie można być w połowie w ciąży. ;)

ja też uważam że zdarzają się projekty w których takie podejście ma rację bytu i nie ma co na siłę wprowadzać zbędnej złożoności przez niepotrzebną abstrakcję,

Oddzielenie prezentacji od logiki to abstrakcja?

najważniejsze jest to by dokonywać świadomego wyboru, i wiedzieć jakie konsekwencje niesie podjęcie danej decyzji.

Pełna zgoda, tylko kto w dzisiejszych czasach programuje świadomie?

1
somekind napisał(a):
neves napisał(a):

Podręcznikowego persistence ignorance może i nie, ale w moim odczuciu "wystarczającego" by móc go rozważać jako kandydata do tej roli.

Albo spełniamy PI albo nie, nie można być w połowie w ciąży. ;)

Ja już dawno wyrosłem z postrzegania wszelakich zasad, metodyk, praktyk czy innych dziwnych tworów w sposób zero jedynkowy. Celem oprogramowania jest dostarczanie wartości biznesowej, więc jeśli nie do końca podręcznikowe rozwiązanie dostarcza tą samą wartość lub prawie tą samą wartość na wszystkich istotnych płaszczyznach co podręcznikowe rozwiązanie, gdzie w przypadku aplikacji biznesowych to przede wszystkim łatwość w utrzymaniu i poprawnośc, to jest dla mnie jak najbardziej akceptowalne.
Szkoda czasu na gonienie za ideałami, mając wystarczająco dobre rozwiązanie;

somekind napisał(a):
neves napisał(a):

ja też uważam że zdarzają się projekty w których takie podejście ma rację bytu i nie ma co na siłę wprowadzać zbędnej złożoności przez niepotrzebną abstrakcję,

Oddzielenie prezentacji od logiki to abstrakcja?

Biorąc pod uwagę jak bardzo szeroko jest definiowany termin abstrakcji to można by to pewnie obronić, nawet pomio tego że pytanie jest tendecyjnie i narzuca w sobie poprawną odpowiedź, zamiast w to brnąć po prostu stwierdze : ViewModele są abstrakcją na dane pochodzące z modelu domenowego.

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