WebAPI rozdzielnie zapytań.

0

Mam coś takiego w bazie danych:

class A :AggregateRoot  {
ICollection<ClassB> ClassB;

} 
class B : Entity{
ClassC ClassC;
}

class C : Entity {}

I mam za zadanie napisać API restowe do zapisywania i pobierania ClassC. Jednak jak widać klasa A to aggregate root.
I teraz jak to zrobić?

Myślę by napisać jeden wielki kontroler dla klasy A który będzie mieć operacje dla ClassA, ClassB i ClassC w jednym kontrolerze. I w programie korzystać np. z operacji na ClassC i wtedy w metodach iść poziom w górę.

Np. post ClassC. Wywołuje post classB a ten wywołuje post classA. Co o tym myślicie? Czy rozdzielić to na trzy inne kontrolery?

Wrzucili nam DDD i mam lekki problem.

2

"Ojej."

DDD... to skomplikowana sprawa :-) do rozwiązywania skomplikowanych domen.

Co do kontrolerów:
Niech ich zakres odpowiedzialności będzie, cóż logiczny i zrobiony z głową. Nie umiem CI pomóc, bo nie wiem jakie jeszcze akcje mają być realizowane. Niech to ma sens. (I wiem, że Ci to nie pomaga, bo dałem Ci ogólną odpowiedź, a sens będzie dla każdego inny, ale za mało wiem, żeby Ci pomóc) Może następna część mojego posta da Ci więcej informacji

DDD i Rest API:
Generalnie używamy DDD żeby ogarnąć logikę domenową. Jeśli nie ma logiki, zwykły CRUD (pobierz encje, zedytuj, zapisz) jest mądrzejszym rozwiązaniem. TO o czym piszesz "Post ClassC" brzmi bardziej jak CRUD -> "weź mi całą (albo prawie) encję ClassC, rzuć na ekran i zedytuj jak chcesz, nie ważna logika i spójność danych (coś tam najwyżej drobnego sprawdzimy) i zapisz mi ją do bazy".
Takie podejście, może wynbikać z tego że źle Cie zrozumiałem, źle napisałeś, albo... po prostu projekt przykładowy jest prosty, i stosowanie DDD jest w nim wymuszone. Zdarza się przy projektach "do nauki".
Możesz używasz REST, nie jako "przeglądarki zasobów", ale dostarczyć metody biznesowe operujące na zasobach (czyli na ClassC nie jako, ale nie bezposrednio - za chwile o tym więcej w następnym akapicie).
To nie powinno być post ClassC. To powinno być Post DoSthSmart(SomeDTO model). I owszem, nie jesteś w tedy w pełni restfull (bo używasz czasowników zamiast rzeczowników), ale to dobrze i ma to sens. I wtedy też inaczej popatrzysz na to co powinno być w danym kontrolerze.

Zapisywanie ClassC:
W DDD mamy agregat roota, między innymi po to, żeby na nim operować a nie na poszczególnych encjach wchodzących w jego skład. Tego w DDD nie powinniśmy robić. To agregat roota pobieramy i jego zapisujemy w całości.
Dlaczego? Bo agregat root ma nam chronić poszczególne encje przed zapisaniem ich w jakiejś niespójnej wersji z resztą systemu. Np załóżmy, że żeby zmienić stan ClassC, musimy mieć specyficzny stan ClassB. Albo w zależności od stanu ClassB w inny sposób zmieniamy stan ClassC. To agregat root ma mieć w sobie jakąś logikę biznesową wymuszającą to i chroniącą nas przed głupotą ;-). Oczywiście, może (często nawet powinien, gdy to możliwe) tę logikę delegować niżej do kolejnych encji, albo najlepiej valueobjectów. Ale to Agregat root jest punktem wejścia do interakcji z jego składowymi.
Np jeśli masz repozytoria, to one powinny być wyłącznie do agregat rootów, nie do wszystkich encji.

Polecam obejrzenie jakiś kursów na pluralsight o DDD:
https://app.pluralsight.com/library/courses/domain-driven-design-fundamentals/table-of-contents
https://app.pluralsight.com/library/courses/domain-driven-design-in-practice/table-of-contents

Jeśli nie masz na to czasu, albo nie masz dostępu to polecam kilka prezentacji Sławka Sobótki (oczywiście, zależy jak bardzo Tobie zależy na "naumianiu" się DDD a ile na zaliczeniu tylko, im więcej tym lepiej)

Te dwa to tak na szybko.
Jeśli chciałbyś więcej materiałów daj znać, ostatnio przygotowywałem spis polecanych materiałów do DDD, widzę, że muszę go znaleźć :)

0

Jasne, że chciałbym się nauczyć ale na spokojnie.

Projekt nie jest do nauki lecz to problem jaki napotkałem w pracy. Przedstawiono mi DDD po łebkach i mam zaimplementować coś takiego jak napisałem wyżej.

Czemu operacje na ClassC jako encji a nie na całym AggregateRoot? Ponieważ zakres sprintu to dodawanie tylko ClassC a "reszta" to jakoś abstrahując. I chce to zrobić tak by nie przebudowywać później całego kontrolera.

Z view po ajaxie kolega wrzuca json ClassC. Ja to muszę podpiąć pod ClassB i wtedy pod ClassA i wtedy zapisać. Tak to rozumiem.

Właśnie co do domeny. Czytałem, że DDD to by łatwo podejść do SKOMPLIKOWANEJ domeny. Jednak domeną jaką realizuje to jakieś 10-20 klas z czego to są zwykłe powiązania 1-1, 1..n jak w podanym przykładzie. Nie ma nie wiadomo czego. Kłóciłem się lekko by tego w DDD nie robić bo to jest proste ale mnie zlano?

Więc wszystkie materiały by się przydały. + dzięki za linki do PS.

1

Jeżeli poprawnie oznaczyłeś aggregate roota, to znaczy, że ClassC nigdy nie ma sensu jako osobny twór. Nie ma więc też sensu jej wczytywanie niezależnie od AR. To tak, jakbyś próbował rozpatrywać pozycję faktury w oderwaniu od faktury. Czego w tej sytuacji nie zrobisz - to nie będzie DDD. Zaś jeśli obiekty ClassC mają sens jako niezależne byty, to znaczy, że w jakimś innym kontekście one mogą być AR i ma sens zrobienie dla nich oddzielnego kontrolera.
Tego nie da się ocenić bez znajomości biznesowego przeznaczenia tych klas.

0

Hmm to dobry punkt spojrzenia!

Tylko jak oddzielić te powiązania? Jak dojść czy to będzie mieć sens. Oczywiście wszystko można zrobić z sensem ale czy to będzie sens? :D

0

To co @somekind napisał. Ciężko poradzić nie znając problemu.
Co do materiałów jutro postaram się wrzucić.
Jakieś wskazówki (może głupoty w zależności jak wygląda pełna logika lub jej brak):
Skoro kolega wrzuca ClassC a nie DTO/viewModel zawierające nieco inne rzeczy niż ClassC to pachnie mocno CRUDem. Można do tego tak więc podejść i jak pisał @somekind zrobić z ClassC agregat roota. A potem zapisać jak w CRUD...

Można też mieć jakąś metodę w ClassA typu UpdateClassC - tylko z sensowną nazwą biznesową, aczkolwiek jeśli to np adres, to UpdateAddress jest ok. Tam podmieniamy ClassC i zapisujemy całego roota... Ale to trudniejsze też może być później do zmiany, jeśli teraz A i B są jakąś abstrakcją. Wtedy możesz już teraz zrobić powiązania między nimi, jeśli rzeczywiście są potrzebne. Albo najpierw olać i zrobić w poprzednim podejściu, a potem zmienić, trudno.

Na marginesie, mam nadzieję, że nie macie logiki biznesowej w kontrolerze? Tylko to tak uprościłeś i wywołujecie tam jakiś serwis, ewentualnie handler, query...? Bo to antypatern, niezależnie czy z DDD czy bez. W DDD zwłaszcza masz możliwość tego nie robić dzięki serwisom i handlerom które i tak będziecie chcieli mieć.

Materiały:
Co do materiałów, tak jak obiecałem:

http://oprogramowaniu.pl/materialy-o-ddd/

Przepraszam jeśli coś nie działa, lub jest kiepsko napisane - późno już a nie dałem rady wcześniej, jutro w nocy postaram się poprawić :-)

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