Co powinny zwracać DAL / Repozytorium?

0

Hej, tak się zastanawiam nad poprawnością rozwiązania, co powinna zwracać warstwa DAL / Repo, jeśli końcowym wynikiem w kontrolerze ma być na ten przykład List<ViewModel>()?

Posiadam warstwy:

  • Controllers (widzi ViewModels, Models, Services),
  • Services (widzi ViewModels, Models, DAL),
  • DAL (widzi Models, DbContext).

Obecnie najpraktyczniejszym rozwiązaniem wydaje mi się takie, gdzie:

  1. Kontroler pyta się serwisu o gotową List<ViewModel>, przekazuje ją do widoków.
  2. Serwis pyta się DAL o IQueryable<Model>, mapuje przez Select na List<ViewModel> i zwraca do kontrolera.
  3. DAL się pyta DB o IQueryable<Model> i takie zwraca do serwisu.

Czy coś byście tu zmienili?

1

a co w momencie gdy bedzie blad ktory programista moze obluzyc? Np uzytkownik nie znalezniony?

Odpowiadajac na pytanie z topicu ja bym zrobil tak:

DAL ma osobny model,
Controller ma swoj wlasny model ktory chce wystawic na API
W serwisie logika + mapowanie. Serwis zwraca Either / Option<T>, a controller sprawdza czy jest successful czy nie

wiec zwracałbym z DAL List<DalModel>

0

View ma zwykle osobny model. Ale nie dlatego ze architektura warstwowa jest najlepsza, tylko raczej dlatego ze widokow do tego samego modelu zwykle jest wiecej niz 1, czasem kilka modeli wiaze sie w jeden widok. Wiec zamiast je mnozyc - rozdziela sie je osobno na model, osobne na dane widoku.

4

Obiekty z DAL powinny zwracać obiekt domenowy (i nie , nie jest to encja z ORM). W warstawie logiki biznesowej obiekty powinny operować tylko obiektach z tej warstwy. Wszystko powinno zależeć od warstwy domeny, domena nie powinna zależeć od niczego innego niż domena. Na warstwie styku można używac interfejsów, tj intefejsy w warstwie domeny a implementacja w warstawie infrastruktury - CurrencyRatioProvider jako interace i robisz sobie RestCurrencyRatioProvider jako implementację. No chyba że robisz CRUDa a rest api to tylko api dostępu do bazy danych - wtedy taka architektura nie jest potrzebna.

0
bakunet napisał(a):
  1. Kontroler pyta się serwisu o gotową List<ViewModel>, przekazuje ją do widoków.

To jest w porządku, o ile opakujesz to w jakieś Result<List<ViewModel>>, które będzie niosło informacje o ewentualnych błędach.

  1. Serwis pyta się DAL o IQueryable<Model>, mapuje przez Select na List<ViewModel> i zwraca do kontrolera.

To mi śmierdzi jakimś generycznym repozytorium. Może się mylę?

  1. DAL się pyta DB o IQueryable<Model> i takie zwraca do serwisu.

Pytasz się bazę bezpośrednio o obiekt domenowy? Powinieneś mieć jakieś encje ORM, czy tam POCOsy, które czytasz z bazy i z nich dopiero tworzyć obiekt domenowy.

0

Trochę uprościłem swój przykład. Generalnie to robię faktycznie jak proponuje @fasadin :
DAL zwraca DAL/Entity Model, w serwisach mam logikę + mapowanie na API Model / ViewModel, Controller zwraca API Model / ViewModel.

maszrum napisał(a):

Pytasz się bazę bezpośrednio o obiekt domenowy? Powinieneś mieć jakieś encje ORM, czy tam POCOsy, które czytasz z bazy i z nich dopiero tworzyć obiekt domenowy.

Używam EF Core, nie napisałem o tym w swoim uproszczonym przykładzie, sorry.

Aleksander32 napisał(a):

Obiekty z DAL powinny zwracać obiekt domenowy (i nie , nie jest to encja z ORM).

Do końca nie rozumiem. Czy obiekt domenowy to jest entity model? Choć to by się kłóciło chyba z encją z ORM.

vpiotr napisał(a):

View ma zwykle osobny model. Ale nie dlatego ze architektura warstwowa jest najlepsza, tylko raczej dlatego ze widokow do tego samego modelu zwykle jest wiecej niz 1, czasem kilka modeli wiaze sie w jeden widok. Wiec zamiast je mnozyc - rozdziela sie je osobno na model, osobne na dane widoku.

To jest jasne, często też ViewModel jest uboższy (mniej wyświetlam w widoku) niż Entity Model i nie chcąc przesyłać całego Entity Model do widoku mapuję na ViewModel w serwisie.

1

Do końca nie rozumiem. Czy obiekt domenowy to jest entity model? Choć to by się kłóciło chyba z encją z ORM.

Nie, obiekt domenowy to obiekt reprezentujący domenę niezależny od struktury bazy danych. Np. w przypadku Jiry taką klasę będzie Issue na którym możesz mieć metody typu assignee gdzie przekazujesz UserId jako argument. Baza danych to szczegół implementacyjny więc DTOs z ORM nie jest obiektem domenowym.
Oczywiście jeśli mówimy o aplikacji biznesowej a nie CRUDzie do CV ;)

0

@Aleksander32:

ale czy takie potrzeby nie wynikają ze słabości np. ORMów?

a co jeżeli mój super-ORM nie potrzebuje NIC - żadnych dodatkowych pól, property czy @ManyToMany (xD)?

a zatem mój domain model i tak już był niezależny, bo jest czystą klasa - tak jakbym do jsona dumpował.

1

No jeśli ORM nie ma takich dziwny i nie wymaga takich pól, konstuktorów domyślnych itp i pola wymagane w obiekcie domenowym zgadzają się 1:1 to jednego to masz rację. Tylko że rzadko tak bywa w prawdziwym świecie ;)

0

@Aleksander32:

konstuktorów domyślnych

nie boli mnie dorzucenie prywatnego pustego konstruktora, nic to raczej nie zmienia w domenie

BTW. konstruktor domyślny =/= konstruktor bezparametrowy :P

0

No zmienia bo może wymóc stsowanie mutowalnych obiektów. Zresztą dlatego ORMy się do obiektów domenowych nie nadają bo wymagają mutowalności (przynajmniej te ze śledzeniem zmian).
Edit:
W Javie konstuktor domyślny jest bezparametrowy.

1

Odpowiadając na pytanie z tematu: repozytorium zwraca aggregate root'a, DAL zwraca coś, bo jest to tak szerokie pojęcie, że nie da się sprecyzować.

bakunet napisał(a):

Obecnie najpraktyczniejszym rozwiązaniem wydaje mi się takie, gdzie:

  1. Kontroler pyta się serwisu o gotową List<ViewModel>, przekazuje ją do widoków.
  2. Serwis pyta się DAL o IQueryable<Model>, mapuje przez Select na List<ViewModel> i zwraca do kontrolera.
  3. DAL się pyta DB o IQueryable<Model> i takie zwraca do serwisu.

Czy coś byście tu zmienili?

Projekcję (użycie Select) najlepiej robić tam, gdzie można zrobić to najefektywniej unikając ładowania zbędnych danych z bazy, a nie delegować do wyższych warstw. IQueryable to jest szczegół implementacji ORMa, to też nie powinno trafiać do wyższych warstw. Po prostu jak już chcesz mieć oddzielną warstwę DAL, to niech ona się zajmuje danymi w pełni.

Sam bym zrobił tak, że kontroler pyta query handlera , a ten pobiera z ORMa dane zmapowane na ViewModel i zwraca do kontrolera. Nie widzę sensu w komplikowaniu dodatkowymi warstwami, które niczego nie wnoszą.

maszrum napisał(a):

Pytasz się bazę bezpośrednio o obiekt domenowy? Powinieneś mieć jakieś encje ORM, czy tam POCOsy, które czytasz z bazy i z nich dopiero tworzyć obiekt domenowy.

No jak się ma sensownego ORMa, a nie z serii Lego Duplo, to oddzielne encje ORMa nie są POCO tylko po co?
Zwłaszcza, że z tego, co rozumiem, to tutaj nie ma domeny, tu jest pobranie danych z bazy i wypchnięcie ich na GUI/API endpoint.

Aleksander32 napisał(a):

No jeśli ORM nie ma takich dziwny i nie wymaga takich pól, konstuktorów domyślnych itp i pola wymagane w obiekcie domenowym zgadzają się 1:1 to jednego to masz rację. Tylko że rzadko tak bywa w prawdziwym świecie ;)

Ale czemu ma się coś zgadzać 1:1? Domena jest jaka jest, a ORM mapuje ją jak wynika ze sposobu przechowywania danych przez bazę. Domeny nie interesuje jakie tabele ORM potworzy, czy np. ValueObject trafi do oddzielnej tabeli, czy będzie składowany w tej samej co encja, co będą zawierały tabele pośredniczące, itd.

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