Non-anemic entities w projektach bez DDD

2

Mam taką encję:

public class Author
    {
        public int Id { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }
        // ....
        public string FullName() => FirstName + LastName;
    }
  1. Czy jeśli nie piszę projektu zgodnie z DDD, to mogę umieścić w kodzie tej encji metodę FullName?
  2. W tym artykule autor sugeruje, żeby logikę niektórych akcji związanych ze zmianą stanu encji umieszczać bezpośrednio w encji (metoda Publish z encji BlogPost). Czy takie mieszanie podejść projektowania aplikacji jest w porządku?
6
  1. Czy jeśli nie piszę projektu zgodnie z DDD, to mogę umieścić w kodzie tej encji metodę FullName?

Tak możesz, świat nie jest czarno biały, istnieją rzeczy pomiędzy anemicznym modelem a bogatym modelem domenowym z DDD. No chyba że ta metoda to na potrzeby widoku jest, wtedy lepszym miejscem jest ViewModel ;).

  1. W tym artykule autor sugeruje, żeby logikę niektórych akcji związanych ze zmianą stanu encji umieszczać bezpośrednio w encji (metoda Publish z encji BlogPost). Czy takie mieszanie podejść projektowania aplikacji jest w porządku?

Nie niektóre, tylko wszystkie, i ma to nawet swoją nazwę, enkapsulacja.

Gdyby każdego było stać na bogaty model domenowy, to tylko taki byśmy oglądali. Niestety nie każdy jest zamożny, więc ludzie pozwalają sobie na tyle na ile ich stać. A jak kogoś na nic nie stać to ma anemiczny model.

3
nobody01 napisał(a):
  1. Czy jeśli nie piszę projektu zgodnie z DDD, to mogę umieścić w kodzie tej encji metodę FullName?

Przede wszystkim FullName powinno być właściwością, a nie metodą.

Czy takie mieszanie podejść projektowania aplikacji jest w porządku?

Generalnie w programowaniu obiektowym chodzi, aby obiekty miały metody. Jeśli obiekt nie ma zachowania, to nie jest obiektem. Całe to DDD to tylko takie głośne marketingowo powtórzenie podstawowych zasad programowania obiektowego, ale nie musisz mieć DDD, żeby mieć obiekty.

Mieszaniem podejść jest za to posiadanie w jednej klasie zarówno publicznych seterów jak i metod zmieniających te same wartości. Łatwo wtedy doprowadzić do niespójnego stanu takiego obiektu.

0
somekind napisał(a):

Mieszaniem podejść jest za to posiadanie w jednej klasie zarówno publicznych seterów jak i metod zmieniających te same wartości. Łatwo wtedy doprowadzić do niespójnego stanu takiego obiektu.

A czy mieszaniem podejść byłoby posiadanie publicznych kolekcji z możliwością zapisu i jednocześnie posiadanie prywatnych seterów dla innych pól?

2

Nie, chodzi o to, żebyś nie miał czegoś w tym rodzaju:

class SuperKlasa
{
    public string Name {get;set;}

   public void SetName(string name)
   {
      Name = name;
   }
}

To może w pewnym momencie doprowadzić do bałaganu. Właściwie to już jest bałagan... No bo powinienem używać właściwości, czy metody do zmiany imienia?
W takim prostym przypadku powinna być dostępna tylko właściwość.

1
nobody01 napisał(a):

A czy mieszaniem podejść byłoby posiadanie publicznych kolekcji z możliwością zapisu i jednocześnie posiadanie prywatnych seterów dla innych pól?

Myślę, że to też mieszanie podejść, bo niby czemu niektóre wartości mają być zenkapsulowane, a inne nie? Dziwne to i chyba niepragmatyczne.

0

Ja ostatnio zacząłem sobie w prywatnym projekcie dla klienta stosować właśnie podejście, ze moje obiekty domenowe (encje) same zarządzają swoim stanem i to one wiedzą, czy można daną czynność na nich wykonać. Chodzi w skrócie właśnie o to, że moja domena posiada wszystkie prywatne settery (private set) właściwości natomiast jak coś chce zmienić to wystawiam metodę, dzięki której z zewnątrz można zmienić jego stan. W tej metodzie następuje uprzednia walidacja czy taką czynność można wykonać na tym obiekcie - chodzi w skrócie o to, żeby dany obiekt był zawsze w stanie valid, umożliwiającym go do operacji na bazie danych (add, update).

Fajnie mi się to sprawdza, gdyż nie ma bezpośrednich operacji na encjach jak to miałem w poprzednim projekcie przez co z czasem zaczęło się wszystko powolutku komplikować.

0

Ja ostatnio zacząłem sobie w prywatnym projekcie dla klienta

Rozumiem, że w pracy architekt zmuszał cię, aby robić na odwrót...? :D

1
W_D_Mam_Twoje_Bany napisał(a):

Ja ostatnio zacząłem sobie w prywatnym projekcie dla klienta

Rozumiem, że w pracy architekt zmuszał cię, aby robić na odwrót...? :D

Nie, w pracy mamy jeszcze inne podejście, co nie znaczy złe, bo uważam je za bardzo fajne. Wg mnie taka styczność z różnymi podejściami jest jak najbardziej na plus, trzeba tylko umieć wyciągać z nich wnioski na przyszłość, zauważać wady i zalety pewnych rozwiązań - dzięki temu przy każdym kolejnym projekcie jesteś w stanie dobrać tę bardziej właściwą metodę i podejście :)

0

Hmm, tylko że jeśli mamy w klasie publiczną kolekcję encji tylko do odczytu i dokonujemy do niej zapisu tylko przez odpowiednią metodę tej klasy (która ma też jakąś logikę), to nie powinniśmy mieć w ogóle dostępu do encji z tej kolekcji poza tą klasą. To znaczy, że musimy wyznaczyć aggregate rooty i (prawdopodobnie) w warstwie aplikacji korzystać z repozytoriów tych aggregate rootów zamiast bezpośrednio z ORMa. Mam rację?

XardasLord napisał(a):

Nie, w pracy mamy jeszcze inne podejście, co nie znaczy złe, bo uważam je za bardzo fajne.

Co to za podejście? ;)

Wg mnie taka styczność z różnymi podejściami jest jak najbardziej na plus, trzeba tylko umieć wyciągać z nich wnioski na przyszłość, zauważać wady i zalety > > > pewnych rozwiązań - dzięki temu przy każdym kolejnym projekcie jesteś w stanie dobrać tę bardziej właściwą metodę i podejście :)

O, właśnie tak samo uważam.

0
XardasLord napisał(a):

Ja ostatnio zacząłem sobie w prywatnym projekcie dla klienta stosować właśnie podejście, ze moje obiekty domenowe (encje) same zarządzają swoim stanem i to one wiedzą, czy można daną czynność na nich wykonać. Chodzi w skrócie właśnie o to, że moja domena posiada wszystkie prywatne settery (private set) właściwości natomiast jak coś chce zmienić to wystawiam metodę, dzięki której z zewnątrz można zmienić jego stan. W tej metodzie następuje uprzednia walidacja czy taką czynność można wykonać na tym obiekcie - chodzi w skrócie o to, żeby dany obiekt był zawsze w stanie valid, umożliwiającym go do operacji na bazie danych (add, update).

No, to ma nawet nazwę - programowanie obiektowe.

nobody01 napisał(a):

Hmm, tylko że jeśli mamy w klasie publiczną kolekcję encji tylko do odczytu i dokonujemy do niej zapisu tylko przez odpowiednią metodę tej klasy (która ma też jakąś logikę), to nie powinniśmy mieć w ogóle dostępu do encji z tej kolekcji poza tą klasą. To znaczy, że musimy wyznaczyć aggregate rooty i (prawdopodobnie) w warstwie aplikacji korzystać z repozytoriów tych aggregate rootów zamiast bezpośrednio z ORMa. Mam rację?

Tak.

0
somekind napisał(a):
XardasLord napisał(a):

Ja ostatnio zacząłem sobie w prywatnym projekcie dla klienta stosować właśnie podejście, ze moje obiekty domenowe (encje) same zarządzają swoim stanem i to one wiedzą, czy można daną czynność na nich wykonać. Chodzi w skrócie właśnie o to, że moja domena posiada wszystkie prywatne settery (private set) właściwości natomiast jak coś chce zmienić to wystawiam metodę, dzięki której z zewnątrz można zmienić jego stan. W tej metodzie następuje uprzednia walidacja czy taką czynność można wykonać na tym obiekcie - chodzi w skrócie o to, żeby dany obiekt był zawsze w stanie valid, umożliwiającym go do operacji na bazie danych (add, update).

No, to ma nawet nazwę - programowanie obiektowe.

Niesamowite :D

0

Tak sobie myślę, że skoro mamy obiekty, mamy agregaty, mamy repozytoria, to do DDD brakuje już chyba tylko dokonania podziału na niezależne od siebie moduły na podstawie bounded contextów. Czy to jest właśnie ta granica pomiędzy po prostu zwykłym programowaniem obiektowym a DDD?

4

Nie bardzo rozumiem, czemu miała by być jakaś granica i czym jest "zwykłe programowanie obiektowe". (Jest jakieś niezwykłe?)

Generalnie gdyby ludzie ogólnie programowali obiektowo, to być może nikt by na wymyślanie takich buzwordów jak DDD nie wpadał, bo nie byłoby potrzeby. Ale, że dla większości programowanie obiektowe opiera się trzymaniu procedur w klasach zamiast w plikach, to tego typu beletrystyka jak książki do DDD znalazła rynek zbytu, i część ludzi czuje się oświecona. Niektórzy nawet tak bardzo, że w podążaniu za ideologią stracili własny rozum i teraz ewangelizują na siłę.
No i chyba tu jakiś problem jajka i kury wystąpił. To nie z faktu posiadania agregatów i repozytoriów wynika DDD, ale jeśli tworzysz zgodnie z DDD, to znaczy, że agregaty i repozytoria będziesz miał.

0

Może mi ktoś powiedzieć, w czym DDD jest lepsze od TS + MediatR? Jakoś trudno mi sobie wyobrazić sytuację, gdzie takie podejście by zawiodło. Ma ktoś jakiś przykład?

  1. Jeśli ktoś pisze testy, to niby w jaki sposób może mieć "encje" w niepoprawnym stanie? Robienie z prostych klas mapujących tabelki w bazie potworków na kilkaset linii z całą logiką biznesową ma być dobre? To ja już wolę robić jakieś TDD w zamian.
  2. Nawet jeśli jakaś logika w handlerach się powtarza, to czy ktoś komuś broni wydzielić jej do jakiejś klasy?
  3. AutoMapper ma swoje zalety: https://4programmers.net/Forum/1566958
1
nobody01 napisał(a):

Może mi ktoś powiedzieć, w czym DDD jest lepsze od TS + MediatR?

No w CRUDzie nie będzie.

  1. Jeśli ktoś pisze testy, to niby w jaki sposób może mieć "encje" w niepoprawnym stanie?

Bardzo prosto, wystarczy, że encja ma pewne właściwości, których możliwe wartości nie mogą występować razem. Np. Zamówienie o Statusie Złożony musi mieć wybraną MetodęDostawy.
Jeśli wszystko opiera się na publicznych seterach i manipulujesz wartościami z zewnątrz, to łatwo możesz utworzyć obiekt w nieoprawnym stanie. No chyba, że w seterach trzymasz logikę, która przed tym broni, ale wtedy to już nie są proste klasy mapowane na tabele.

Robienie z prostych klas mapujących tabelki w bazie potworków na kilkaset linii z całą logiką biznesową ma być dobre? To ja już wolę robić jakieś TDD w zamian.

Klasy mapujące tabelki to jest temat wtórny w ogóle do encji i DDD. Można mieć w jednym projekcie obie te rzeczy, i nawet mapować jedne na drugie według potrzeb. Encja może być obiektem tworzonym i żyjącym tylko na czas realizacji jakiejś operacji biznesowej, nie musi jednocześnie służyć do utrwalania w bazie.
Nie wiem też skąd pomysł, ze logika biznesowa w encjach ma zajmować kilkaset linii. To jest zły pomysł niezależnie od użytych wzorców.
A TDD wypada robić zarówno w DDD, jak i TS, jak i bez żadnego z tych. Byle z sensem.

0
somekind napisał(a):

No w CRUDzie nie będzie.

Dlaczego tylko przy CRUDzie? Jakiś przykład?

nobody01 napisał(a):
  1. Jeśli ktoś pisze testy, to niby w jaki sposób może mieć "encje" w niepoprawnym stanie?

Bardzo prosto, wystarczy, że encja ma pewne właściwości, których możliwe wartości nie mogą występować razem. Np. Zamówienie o Statusie Złożony musi mieć wybraną MetodęDostawy.
Jeśli wszystko opiera się na publicznych seterach i manipulujesz wartościami z zewnątrz, to łatwo możesz utworzyć obiekt w nieoprawnym stanie. No chyba, że w seterach trzymasz logikę, która przed tym broni, ale wtedy to już nie są proste klasy mapowane na tabele.

Nie rozumiem, przecież w testach sprawdzę, czy walidacja operacji przebiega poprawnie, więc w czym problem?

0

Nie rozumiem, przecież w testach sprawdzę, czy walidacja operacji przebiega poprawnie, więc w czym problem?

A niby jak walidacja ma przed tym bronić?

Dlaczego tylko przy CRUDzie? Jakiś przykład?

Nie wiem zapytaj się chrumczoka to on napisał, że po tylu latach pracy może stwierdzić, że nie ma nic lepszego od MediaR z TS bo to takie SRP.

0

No chyba taki jest cel walidacji - sprawdzenie poprawnosci danych wejsciowych i mozliwosci wykonania operacji.

2

Tak, tylko przy oddzielonej walidacji nie masz pewności czy ktoś Ci jej nie postanowi ominąć lub nie zmieni właściwości już po zwalidowaniu, "bo coś". Mając sprzężoną logikę ze zmianami tego unikniesz.

I oczywiście, można stwierdzić, że ktoś sobie wtedy również zmieni logikę, ale to już trudniej przeoczyć w zmianach niż jakieś random owe ustawienie czegoś, bez przeszukiwania wszystkich zmian i dochodzenia czy ten stan jest sprawdzany czy nie.

0

Ale ja tu caly czas pisze, ze takie rzeczy mozna i trzeba testowac.

2

Co nie zmienia faktu, że jak masz klasy z właściwościami i do tego zewnętrzne "serwisy" operujące na tychże, razem z zewnętrznymi walidatorami i diabli wiedzą czym jeszcze, to nie jest to obiektówka, tylko oranie procedurami po strukturach.

I tak, zgadzam się, że należy testować. Ale łatwiej przetestować zachowanie jednego obiektu niż tworzyć 4, żeby wykonać jedną prostą operację. A potem ktoś sprzęgnie serwis na twardo z bazą i zaczynasz połowę operacji mockować, bo jest szybciej i łatwiej, w efekcie testując mocki albo walisz integracyjne...

Zwyczajnie łatwiej zrobić dziadostwo moim zdaniem.

0

To, ze to nie jest do konca obiektowe, nie znaczy od razu, ze jest zle. W DDD do serwisow aplikacyjnych tez trzeba pisac testy, bo ta walidacja w encjach jest tylko na wszelki wypadek, gdyby warstwa aplikacji zawiodla. Roznica jest taka, ze przy TS nie mamy duplikacji kodu i nic nie wnoszacych wrapperow na ORMa.

1

A to ja nie mówię, że od razu złe, tylko że łatwiej się zaplątać moim zdaniem. I nie tyle "nie do końca obiektowe" co zwyczajnie proceduralne, bo "zachowanie" jest oderwane od danych, więc nie masz obiektów, tylko struktury i procedury operujące na tychże.

0

Ok, niech ktoś pokaże duży projekt napisany zgodnie z DDD (albo obiektowo, jak ktoś nie chce buzzwordów).

0

Masz chciałeś jakiś większy projekt dobrze zrobiony w DDD https://github.com/VaughnVernon/IDDD_Samples_NET

0
nobody01 napisał(a):

Dlaczego tylko przy CRUDzie? Jakiś przykład?

Nie napisałem, że tylko w CRUDzie. CRUD jest przykładem czegoś, do czego DDD nie ma zastosowania.

Nie rozumiem, przecież w testach sprawdzę, czy walidacja operacji przebiega poprawnie, więc w czym problem?

W tym, że testy nie sprawdzą, czy programista wywołuje tę walidację zawsze podczas majstrowania przy obiekcie.

nobody01 napisał(a):

No chyba taki jest cel walidacji - sprawdzenie poprawnosci danych wejsciowych i mozliwosci wykonania operacji.

Owszem - danych wejściowych. Podczas wykonywania logiki biznesowej, nie operujesz na danych wejściowych tylko na regułach/przepływach biznesowych.

nobody01 napisał(a):

Ale ja tu caly czas pisze, ze takie rzeczy mozna i trzeba testowac.

Podobno można też pisać w językach dynamicznie typowanych i zastąpić system typów testami.

nobody01 napisał(a):

To, ze to nie jest do konca obiektowe, nie znaczy od razu, ze jest zle. W DDD do serwisow aplikacyjnych tez trzeba pisac testy, bo ta walidacja w encjach jest tylko na wszelki wypadek, gdyby warstwa aplikacji zawiodla.

Oj nie, Ta walidacja służy do czegoś zupełnie innego.

Roznica jest taka, ze przy TS nie mamy duplikacji kodu i nic nie wnoszacych wrapperow na ORMa.

Bynajmniej. To w projektach opartych o TS jest masa niczego nie wnoszących wrapperów. W DDD nie ma takich, tylko projektów w DDD nie ma.

1

Oj nie, Ta walidacja służy do czegoś zupełnie innego.

Można powiedzieć, że jest to forma walidacji, ale tak naprawdę nie chodzi o walidację tylko o kontrakt. Według mnie nie powinno się tego nazywać walidacją. Nazywanie tego walidacją rodzi pewnego rodzaju problem logiczny typu "a po co mi dwie walidacje...."

Bynajmniej. To w projektach opartych o TS jest masa niczego nie wnoszących wrapperów. W DDD nie ma takich, tylko projektów w DDD nie ma.

DDD nie mówi nic o tym, że nie możesz używać takich komponentów, jakie ci się podobają w warstwie Applikacji, jeśli o to ci chodzi. To że ktoś pakuje coś w wrapa i nie wie poco to jego problem.

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