W jakiej warstwie tworzyć ViewModels?

0

Projektuje aplikacje i mam taki podział:
project.Web,
project.BLL,
project.DAL,
project.Repo,
project.Services,
project.ViewModels
Zastanawiam się w której warstwie tworzyć view model ?
Jeżeli w kontrolerze, to na czym wtedy operować w services?

  • Dodatkowe pytanie Identity z Core wygenerować w której warstwie? (wydaje mi się, że web)
3

ViewModele trzymasz blisko warstwy prezentacji. Według mnie serwisy nie muszą otrzymywać view modeli, szczególnie takich udekorowanych jakimiś adnotacjami, dodatkowymi polami potrzebnymi tylko do obsłużenia widoku. Powiedzmy, że posiadasz CarService z metodą CreateCar(CreateCarViewModel model). Możesz to zamienić na CreateCar(string name, string model, ... etc). Jeżeli tych parametrów jest więcej możesz stworzyć DTO ze wszystkimi parametrami w project.Service. Wywołanie metody wyglądałoby tak: CreateCar(CreateCarDto createCar). Na początku może się wydawać, że wracamy do punktu wyjścia, ale często w aplikacjach webowych używamy jakiś radio buttonów, droplist i taki ViewModel nie ma racji bytu poza warstwą prezentacji.

https://refactoring.com/catalog/introduceParameterObject.html

3

Przerabiałem obie opcje czyli trzymałem VM w osobnym projekcie jak i blisko warstwy prezentacji, czyli w projekcie webowym. Z tego doświadczenia zdecydowanie podpisuje się pod tym, co napisał Kolega wyżej. Trzymanie VM w osobnym projekcie może rodzić pewne problemy jak np. circular dependency kiedy przyszło pisać własny binder i skorzystać z jakichś helperów dostępnych tylko w projekcie webowym. Przekonałem się wtedy, że folder Models w MVC po coś jednak jest :D Teraz trzymam VM jak najbliżej, czyli na poziomie Area, a jak coś potrzebuje globalnie to jest folder Models w rootcie projektu.
Kolejna sprawa o której pisze Kolega wyżej - web service nie powinien przyjmować VM właśnie ze względu na jakieś dodatkowe atrybuty jak np. DisplayName czy nawet dodatkowe property, które są wymuszane poprzez np. technologię frontendową. Co więcej, data model może wymagać swoich specyficznych atrybutów, które nie powinny znaleźć się w VM :D Po to mamy narzędzia do mapowania obiektów jak np. Mapster. Wtedy zachowujemy bez większych problemów separację warstwy logicznej i prezentacji.

1
dawid75_75 napisał(a):

Trzymanie VM w osobnym projekcie może rodzić pewne problemy jak np. circular dependency kiedy przyszło pisać własny binder i skorzystać z jakichś helperów dostępnych tylko w projekcie webowym.

W ASP.NET Core nie trzeba używać atrybutów do customowego bindingu: https://docs.microsoft.com/pl-pl/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-3.0, więc nie ma problemu, że ViewModels mają referencję do Web. O co chodzi z tymi helperami?

IMO pomysł wrzucenia viewmodeli do osobnego projektu @somekind jest zazwyczaj bardzo pragmatyczny. Ostatnio przekonałem się, że utrzymywanie dwóch warstw: DTO i viewmodeli jednak trochę kosztuje i warto się zastanowić, czy taki podział jest potrzebny (np. gdy korzystamy z MediatR).

0

Okej, czyli serwisy lepiej by nie otrzymywały ViewModels, a w takim razie repozytoria powinny zwracać surowe dane z Entities, czy mogą ViewModels?

0

Jeśli z repozytorium chcesz zwracać gotowe ViewModele, to po co Ci w takim razie serwisy?

0

Rozumiem, jeszcze mnie zastanawia, czy dane zwracane przez Repezytoria powinny być IQueryable, czy może konwerterować je przed zwróceniem np. do Listy?

0

Osobiście zawsze praktykowałem IQuerable i mapowanie do DM na poziomie repozytorium. W aplikacji klienckiej mapowanie do VM.
Chętnie również dowiem się jak praktykują bardziej doświadczeni Koledzy.

0

Repozytoria powinny zwracać dane, IQueryable to nie są dane tylko niewykonane jeszcze zapytanie do bazy. Nie powinieneś też zwracać z repo entity. Powinieneś zwracać DTO. A jak sie ma DTO do Viewmodelu? Rożnica jest taka że VM to gotowy obiekt który przesyłasz do widoku. A DTO które zwracasz z repo do serwisu możesz jeszcze w jakiś sposób obsłużyć.

0
kzkzg napisał(a):

Repozytoria powinny zwracać dane, IQueryable to nie są dane tylko niewykonane jeszcze zapytanie do bazy. Nie powinieneś też zwracać z repo entity. Powinieneś zwracać DTO. A jak sie ma DTO do Viewmodelu? Rożnica jest taka że VM to gotowy obiekt który przesyłasz do widoku. A DTO które zwracasz z repo do serwisu możesz jeszcze w jakiś sposób obsłużyć.

Tutaj mam pytanie - a co w przypadku jak w tabeli mam dziesiątki tysięcy rekordów i potrzebuję jedynie jakąś krótką, stronicowaną listę? IQuerable wydaje mi się tutaj odpowiedni w kontekście takim, że z tego co czytalem na poziomie repozytorium nie powinno się obsługiwać sortowania i stronicowania.

0

Jeśli mam WebService, Service i repo to moim zdaniem na poziomie warstwy serwisu.

2

Z repozytorium powinienes korzystać za zasadzie że masz metody tego repo i dostajesz jakieś dane. To czy tam w repo jest EF, albo klejone są gołe zapytania, czy też może w ogóle nie ma bazy danych tylko dane sa z pamięci, albo czytane są z pliku - to nie ma znaczenia. Ty zakładasz że jest tam EF. I nie dostajesz danych tylko (częściowe) zapytanie.

2
balti napisał(a):

Okej, czyli serwisy lepiej by nie otrzymywały ViewModels, a w takim razie repozytoria powinny zwracać surowe dane z Entities, czy mogą ViewModels?

Repozytoria mają zwracać encje domenowe. (Nie mylić z tym, co w EF nazywane jest jako entity.)

balti napisał(a):

Rozumiem, jeszcze mnie zastanawia, czy dane zwracane przez Repezytoria powinny być IQueryable, czy może konwerterować je przed zwróceniem np. do Listy?

Repozytorium powinno być zdatne do użytku dla logiki biznesowej, więc nie może wypuszczać szczegółów implementacji na zewnątrz.

dawid75_75 napisał(a):

Tutaj mam pytanie - a co w przypadku jak w tabeli mam dziesiątki tysięcy rekordów i potrzebuję jedynie jakąś krótką, stronicowaną listę? IQuerable wydaje mi się tutaj odpowiedni w kontekście takim, że z tego co czytalem na poziomie repozytorium nie powinno się obsługiwać sortowania i stronicowania.

Owszem, a to dlatego, że sortowanie i stronicowanie to są potrzeby widoku, a repozytorium nigdy żadnego związku z widokiem nie ma.

http://commitandrun.pl/2016/05/11/Repozytorium_najbardziej_niepotrzebny_wzorzec_projektowy/

Jeśli celem jest pobranie danych na potrzeby widoku, to trzeba sobie po prostu napisać klasę, która dane z bazy weźmie, przekształci na viewmodele i zwróci, a nie udawać DDD z serwisami i repozytoriami, które w takim przypadku w ogóle nie mają zastosowania.

0

"Repozytorium powinno być zdatne do użytku dla logiki biznesowej, więc nie może wypuszczać szczegółów implementacji na zewnątrz."
Czyli w poniższym przypadku wypuszczam szegóły implementacji na zewnątrz i tak się nie powinno robić, czyli lepiej było by tu zwracać np. IEnumerable zamiast IQueryable?
W przypadku stronicowania zamykam je w ViewModel co jest prawidłowe?
Wydaje mi się, że jak zwracam sobie produkty z repo to już wtedy powinienem selecta zrobić i opakować w konkretny ViewModel, a potem przekazać do ViewModelu PaggingInfo z logika stronicowania?

przykład:
ProductRepository:

    public IQueryable<Product> Products => context.Products;

    public ViewResult List(string category, int productPage = 1) =>
      View(new ProductsListViewModel
      {
        Products = repository.Products
                             .Where(p => category == null || p.Category == category)
                             .OrderBy(p => p.ProductID)
                             .Skip((productPage - 1) * PageSize)
                             .Take(PageSize),
        PagingInfo = new PagingInfo
        {
          CurrentPage = productPage,
          ItemsPerPage = PageSize,
          TotalItems = category == null ?
          repository.Products.Count() :
                    repository.Products
                    .Where(e => e.Category == category)
                    .Count()
        },
        CurrentCategory = category
      }
0
balti napisał(a):

"Repozytorium powinno być zdatne do użytku dla logiki biznesowej, więc nie może wypuszczać szczegółów implementacji na zewnątrz."
Czyli w poniższym przypadku wypuszczam szegóły implementacji na zewnątrz i tak się nie powinno robić, czyli lepiej było by tu zwracać np. IEnumerable zamiast IQueryable?

Tak, bo IQueryable to jest w tym wypadku szczegół implementacji ORMa, a logika biznesowa nie powinna nic o nim wiedzieć.

W przypadku stronicowania zamykam je w ViewModel co jest prawidłowe?
Wydaje mi się, że jak zwracam sobie produkty z repo to już wtedy powinienem selecta zrobić i opakować w konkretny ViewModel, a potem przekazać do ViewModelu PaggingInfo z logika stronicowania?

Skoro potrzebujesz viewmodeli, to nie powinieneś mieć repozytorium wcale. Repozytorium ma dostarczać obiektów do przetwarzania w jakichś procesach biznesowych, a nie do wyświetlania na ekranie.

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