Wzorzec repozytorium

Odpowiedz Nowy wątek
2013-04-07 22:37

Rejestracja: 7 lat temu

Ostatnio: 5 lat temu

0

Witam

Przeglądam wzorce i jak zwykle mam wątpliwości jeżeli chodzi o moje pomysły. Sprawa prosta jak drut, w każdym tutorialu, opisie jest klasa i metody, które zwracają obiekty tej klasy:

http://patryknet.blogspot.com[...]pository-pattern-wzorzec.html

np. FakePersonRepository ( metoda GetById ) zwraca obiekt klasy Person, ale co w przypadku kiedy moje zapytanie złącza kilka tabel i zawiera kilka pól z tych tabel ( nie tylko pola z jednej), czy interfejs IPersonRepository może zwracać oprócz void-a i T, typ object? ( pewnie może, ale pytanie jak to ma się do dobrych zasad programowania)

Pozostało 580 znaków

Błękitny OrzełC
2018-03-21 10:22
Błękitny OrzełC
1

Ciekawa dyskusja.

Właśnie w moim projekcie wywalam Repository z nazw i wstawiam Dao :P
Bo nie robią nic tylko zwracają dane z "bazy"(webApi) i mapują na moje DTO.

Pozostało 580 znaków

Czarny Szewc
2018-07-03 17:38
Czarny Szewc
0

Co jest złego w Repository?

Zaczyna się od CustomerRepository z metodami GetCustomerById, GetCustomerByName, GetCustomerByNIP. Wszystko jest fajnie. Potem okazuje się, że trzeba pobrać klienta razem z fakturami, więc ktoś zmienia zawartość tych metod tak, że zachłannie pobierane są faktury. Oczywiście nigdzie w kodzie tego nie widać, do repozytorium nikt inny nie zagląda (bo po co?), ale ludzie dziwią się, że w wielu miejscach aplikacja zwolniła... Na szczęście znalezienie przyczyny nie trwa długo, i trzeba to naprawić. Więc co się robi? Nowe metody o więcej mówiących nazwach: GetCustomerWithInvoicesById, GetCustomerWithInvoicesByName, GetCustomerWithInvoicesByNIP. I jest wszystko dobrze!
Przynajmniej na razie, bo z czasem pojawia się potrzeba pobrania klientów wg adresu wraz z jego pracownikami, ale bez faktur: GetCustomersWithEmployeesButWithoutInvoicesByAddress, a potem potrzeba pobrania wszystkich klientów, z fakturami, bez pracowników, z ulubionymi filmami, bez kochanek wg grupy krwi żony i imienia kota córki prezesa: GetCustomersWithInvoicesWithoutEmployeesWithFavouriteMoviesWithoutMistressesByWifeBloodTypeAndChairmanDaughterCatName.
Im bardziej skomplikowana domena tym więcej potrzeb kombinacji warunków i wyciągania powiązanych encji, tym więcej metod w klasie Repozytorium. Kończymy z klasą z milionem metod... i to ma być dobre?

A jak masz listę faktur do wyświetlenia to jak to robisz? Założmy, że masz jakiegoś ORM i wtedy w tym kontrolerze bezpośrednio piszesz zapytanie? Jeśli nie to jak to u Ciebie wygląda?

Pozostało 580 znaków

2018-07-04 01:37
Moderator

Rejestracja: 11 lat temu

Ostatnio: 1 godzina temu

Lokalizacja: Wrocław

0

Żadnych ORMów ani zapytań w kontrolerach. Na ORMie operuję w jakichś klasach z warstwy aplikacji. Jeśli mowa o odczycie danych na potrzeby GUI, to wręcz taką klasę nazywam viewmodelproviderem.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

Trzeźwy Kura
2018-07-05 13:29
Trzeźwy Kura
0

@somekind: Mam kilka pytań.
1) Jak implementujesz operacje CRUDowe, jeśli nie masz repozytoriów, a kontrolery nie wiedzą nic o ORMie? Tworzysz klasy typu AddXService, UpdateXService itd., a w kontrolerach odwołujesz się do ich interfejsów tych klas?
2) W jaki sposób budujesz zapytania i je egzekwujesz?
3) Gdzie mapujesz encje na DTO?
4) Gdzie jest walidacja danych wejściowych? Usługi zwracają jakieś enumy określające status operacji?

A, i jeszcze jedno. Nie wiem, czy to Ty jesteś autorem tego artykułu (2016 rok): http://commitandrun.pl/2016/0[...]potrzebny_wzorzec_projektowy/, ale tekst w sekcji 'Rozrost Repozytoriów' jest skopiowany z Twojego postu na początku tego wątku (2013 rok). ;)

no i się wydało :P - a_s_f 2018-07-05 13:37
stalking team seal of approval ;) - Shalom 2018-07-05 16:15
przecież już chyba dwa miechy temu pisałem, że fajnego mosz bloga, dejwid :D - WeiXiao 2018-07-05 17:18

Pozostało 580 znaków

._.
2018-07-05 16:23
._.

Rejestracja: 1 rok temu

Ostatnio: 1 rok temu

0
Trzeźwy Kura napisał(a):

1) Jak implementujesz operacje CRUDowe, jeśli nie masz repozytoriów, a kontrolery nie wiedzą nic o ORMie? Tworzysz klasy typu AddXService, UpdateXService itd., a w kontrolerach odwołujesz się do ich interfejsów tych klas?
2) W jaki sposób budujesz zapytania i je egzekwujesz?
3) Gdzie mapujesz encje na DTO?
4) Gdzie jest walidacja danych wejściowych? Usługi zwracają jakieś enumy określające status operacji?

Zapytanie oraz mapowanie generuje algorytm. Ja tylko podaje do niego początkowe wartości jak restrykcja filtra i jego reguły oraz listę pól, które chcę pobrać. Algorytm filtrowania mam zamodelowany jako Domain Model, mapowanie oraz ORM chowam za abstrakcją DataQueryProvider. ReaderDtoApplicationService woła DataQueryProvider oraz Filtr z Domeny, który konfiguruje kryteria filtrowania i podaje je do Provaidera. StorageOperation implementuje w infrastrukturze a UnitOfWork steruje Contener IOC.

A algorytm mapowania, jak i wybierania zapytania SQL bazuje na zbieżności nazw pól pomiędzy PersistenceMode a DataReslut dla Generycznego DTO, które wzbogacam o metadane dla widoku.

Mam nadzieję, że pomogłem...

edytowany 1x, ostatnio: ._., 2018-07-05 16:27

Pozostało 580 znaków

2018-07-05 17:17
Moderator

Rejestracja: 11 lat temu

Ostatnio: 1 godzina temu

Lokalizacja: Wrocław

0
Trzeźwy Kura napisał(a):

@somekind: Mam kilka pytań.
1) Jak implementujesz operacje CRUDowe, jeśli nie masz repozytoriów, a kontrolery nie wiedzą nic o ORMie? Tworzysz klasy typu AddXService, UpdateXService itd., a w kontrolerach odwołujesz się do ich interfejsów tych klas?

Staram się raczej nie używać sufiksu "Service". Wiele zależy od konkretnego przypadku, czasem np. Add/Update/Delete warto trzymać razem. No i nie używam interfejsów, bo one zwyczajnie nie mają sensu we własnym kodzie.

2) W jaki sposób budujesz zapytania i je egzekwujesz?

Korzystając z API dostarczanego przez ORM. Nie wiem czy o to Ci chodziło w pytaniu, bo jest zbyt ogólne jak dla mnie.

3) Gdzie mapujesz encje na DTO?

Jak najbliżej bazy danych. Przy wykorzystaniu z ORMa jest to miejsce wykonywania projekcji, czyli zazwyczaj metoda Select.

4) Gdzie jest walidacja danych wejściowych? Usługi zwracają jakieś enumy określające status operacji?

Różnie z tym jest, zazwyczaj wygląda to tak, że warstwa logiki aplikacji/biznesowej sprawdza sensowność podanych argumentów i rzuca wyjątki, gdy coś jest nie tak. Dokładniejsza walidacja wejścia odbywa się w warstwie prezentacji, gdzie sprawdzane są wszystkie reguły i do użytkownika zwracana jest lista komunikatów/kodów błędów.

A, i jeszcze jedno. Nie wiem, czy to Ty jesteś autorem tego artykułu (2016 rok): http://commitandrun.pl/2016/0[...]potrzebny_wzorzec_projektowy/, ale tekst w sekcji 'Rozrost Repozytoriów' jest skopiowany z Twojego postu na początku tego wątku (2013 rok). ;)

Wielkie umysły myślą podobnie.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

Trzeźwy Kura
2018-07-05 17:48
Trzeźwy Kura
0

@somekind: Dziękuję, zaczyna mieć dla mnie sens to, co mówisz, ale mam jeszcze kilka pytań. Z tego, co zrozumiałem, w kontrolerach wykorzystuję usługi, które korzystają z pewnych data providerów, a gdy coś pójdzie nie tak, rzucany jest wyjątek, który przechwytuje kontroler, a następnie zwraca odpowiedni rezultat. Tylko zastanawia mnie, w czym taki data provider jest lepszy od "typowego" repozytorium (poza tym, że nie udaje czegoś, czym nie jest). W końcu wraz z rozwojem aplikacji metod będzie przybywać, więc trzeba te providery jakoś sensownie podzielić na mniejsze klasy. W pytaniu drugim chodziło mi o to, jak to zrobić, by było dobrze ;) Albo może tworzyć jakieś obiekty zapytań typu GetXByYQuery i przekazywać je do metod providerów? Btw świetnego masz tego bloga. ;)

Pozostało 580 znaków

2018-07-06 17:47
Moderator

Rejestracja: 11 lat temu

Ostatnio: 1 godzina temu

Lokalizacja: Wrocław

5

Przykładowy pseudokod do dalszych rozważań:

public class CustomerController
{
    private CustomerViewModelProvider provider;
    private CustomerStore store;

    // odpowiedni konstruktor

    public DataPage<CustomerViewModel> Index(DataPageRequest<CustomerViewModel> request)
    {           
        return provider.GetDataPage(request);
    }

    public CustomerDetailsViewModel Details(long customerId)
    {
        return provider.Get(customerId);
    }

    public CreationResult Create(CustomerCreateModel customer)
    {
        return store.Create(customer);
    }

    public DeleteResult Delete(long id)
    {
        return store.Delete(id);
    }   

    public OperationResult WinThePrize(PrizeRequest request) // jest po 17 w piątek, idę na piwo i nie mam sensownego pomysłu
    {
        return customerService.Win(request);
    }
}
public class CustomerService
{
    public Win(PrizeRequest request)
    {
        // session to obiekt jakiegoś ORMa, niekoniecznie istniejącego
        var customer = session.Get<Customer>(request.CustomerId);
        var products = session.GetAll<Product>(request.ProductCategory, request.MinimumPrice);

        var magicService = new DrawingService(products);        
        customer.Prize = magicService.GetPrize();

        session.CommitAndFlush();
    }
}

DataPageRequest - generyczna klasa zawierająca takie pola jak numer strony do pobrania, liczba pozycji na stronę i warunki filtrowania (np. jako słownik nazwa właściwości: wartość filtra) oraz warunki sortowania (np. jako słownik nazw właściwości i kierunku sortowania).
DataResponse - lista viewmodeli zmapowanych z bazy, numer strony, wielkość strony, liczba wszystkich stron.

Trzeźwy Kura napisał(a):

@somekind: Dziękuję, zaczyna mieć dla mnie sens to, co mówisz, ale mam jeszcze kilka pytań. Z tego, co zrozumiałem, w kontrolerach wykorzystuję usługi, które korzystają z pewnych data providerów, a gdy coś pójdzie nie tak, rzucany jest wyjątek, który przechwytuje kontroler, a następnie zwraca odpowiedni rezultat.

No ja bym raczej widział, że do pewnych rzeczy są serwisy, do pewnych providery, do jeszcze innych inne klasy. Nie ma po co tworzyć warstw wrapperów.
A wyjątki przechwytuje filtr albo handler, nie ma po co dręczyć tym kontrolery.

Tylko zastanawia mnie, w czym taki data provider jest lepszy od "typowego" repozytorium (poza tym, że nie udaje czegoś, czym nie jest). W końcu wraz z rozwojem aplikacji metod będzie przybywać, więc trzeba te providery jakoś sensownie podzielić na mniejsze klasy.

Ale kiedy niby będą przybywały metody?
Logika biznesowa nie korzysta z providerów, jedyne przypadki użycia mając wpływ na ich liczność, to nowy ekran w GUI.
Jeśli napiszesz ViewModelProvider niegenerycznie, to będą przybywały nowe klasy providerów.
Jeśli napiszesz generycznie, to nie będzie przybywać ani metod ani klas providerów.

W pytaniu drugim chodziło mi o to, jak to zrobić, by było dobrze ;) Albo może tworzyć jakieś obiekty zapytań typu GetXByYQuery i przekazywać je do metod providerów?

Też można - jeśli oczywiście masz jakieś "sztywne" ekrany w aplikacji, które nie pozwalają użytkownikowi na przeszukiwanie samodzielnie.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."
Racja Service dopisuje się po to, aby określić zachowanie obiektu. Jeśli dopisujesz Provider, Reader, no to nie ma sensu Dodawać tam Service, bo wiadomo, że nie jest to "Rzecz". - ._. 2018-07-07 11:50

Pozostało 580 znaków

._.
2018-07-07 11:43
._.

Rejestracja: 1 rok temu

Ostatnio: 1 rok temu

0

Nawiązując do wypowiedzi @somekind, chciałbym dodać, że...

Aby to wszystko było zgodne z SOC, powinieneś trzymać ViewModele w warstwie pośredniej pomiędzy logiką "biznesową, aplikacji" a warstwą prezentacji. Może to trochę przypominać pojęcie kontraktów z SOA albo prościej, możesz na to patrzeć jak na warstwę Common. Innym wyjściem jest zrobienie oddzielnej warstwy z ReaderViewModel (Osobiście mi się to nie podoba). Mógł byś również zdefiniować interface dla Readera w warstwie prezentacji ale to bez sensu.

Dalej jeszcze o SOC, jeśli w warstwie prezentacji nie renderujesz HTML i chcesz zwrócić zwykłe DTO - Resources (mniejsza o to jak to zwał) możesz sobie zrobić w warstwie prezentacji Dekorator na Readera, który doda jakieś dodatkowe elementy jak np. Linki na styl HATEOAS.

Chciałbym również zacząć pisać własnego bloga, w którym będę między innymi opisywał jak zrobić generyczną bibliotekę dla operacji CRUD. Chcielibyście coś takiego czytać...?

edytowany 3x, ostatnio: ._., 2018-07-07 16:57

Pozostało 580 znaków

2018-07-07 23:22
Moderator

Rejestracja: 11 lat temu

Ostatnio: 1 godzina temu

Lokalizacja: Wrocław

0
._. napisał(a):

Aby to wszystko było zgodne z SOC, powinieneś trzymać ViewModele w warstwie pośredniej pomiędzy logiką "biznesową, aplikacji" a warstwą prezentacji.

Można tak robić, i bardzo często ma to sens, ale nie trzeba. Kontrolery mogą np. składać viewmodele z danych otrzymanych z warstwy aplikacji.


"HUMAN BEINGS MAKE LIFE SO INTERESTING. DO YOU KNOW, THAT IN A UNIVERSE SO FULL OF WONDERS, THEY HAVE MANAGED TO INVENT BOREDOM."

Pozostało 580 znaków

Odpowiedz

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