Kiedy używać wzorca repozytorium

20

Niedawno przeczytałem, że stosowanie wzorca repozytorium w połączeniu z ORM nie jest dobrą praktyką. Z mojego zrozumienia wynika, że ​​abstrakcja, którą zapewniają w bazie danych SQL, jest zbyt nieszczelna, aby mogła zostać zawarta we wzorcu.

Mam kilka pytań na ten temat:

  1. Co robisz, jeśli chcesz zmienić ORM? Miałbyś kod specyficzny dla ORM w swojej aplikacji, gdybyś nie zawierał go w repozytorium.

  2. Czy wzorzec repozytorium jest nadal prawidłowy, gdy nie używasz ORM i sam używasz ADO.NET do dostępu do danych i zapełniania danych obiektowych?

  3. Jeśli używasz ORM, ale nie wzorca repozytorium, gdzie przechowujesz często używane zapytania? Czy mądrze byłoby reprezentować każde zapytanie jako klasę i mieć jakąś fabrykę zapytań do tworzenia instancji?

użytkownik 1450877
źródło
1
1) nigdy nie będziesz w stanie zamienić ORM z powodu ich różnych zachowań, nawet jeśli oba obsługują linq, będą zachowywać się na tyle odmiennie, że aplikacja się zepsuje. np. leniwe ładowanie, brudne śledzenie, serwery proxy itp. Fajnie jest jednak móc wymienić ORM na implementację w memie do testowania.
Roger Johansson
Jeśli chodzi o to, co warto, moje zdanie na temat repozytorium / ORM jest tutaj: stackoverflow.com/questions/13180501/...
Eric King
Gdzie przeczytałeś, że jest to zła praktyka?
Dave Hillier

Odpowiedzi:

3

1) To prawda, ale jak często zmieniasz ORM?
2) Powiedziałbym tak, ponieważ kontekst ORM jest swego rodzaju repozytorium i kryje w sobie wiele pracy związanej z tworzeniem zapytań, wyszukiwaniem danych, mapowaniem itp. Jeśli nie używasz ORM, ta logika wciąż musi gdzieś znajdować się. Nie wiem jednak, czy kwalifikowałoby się to jako wzorzec repozytorium w najściślejszym znaczeniu tego słowa ...
3) Często enkapsuluję zapytania, ale zwykle jest to więcej do celów testowania / stubowania. Poza tym byłbym ostrożny przy ponownym użyciu zapytania w różnych częściach aplikacji, ponieważ wtedy ryzykujesz powstanie zależności od czegoś, co może zmienić n-razy (n to liczba miejsc, w których używasz zapytania).

Stefan Billiet
źródło
1
1) Zadajesz złe pytanie. Nie ma znaczenia, jak często musi to być tylko raz. Biorąc pod uwagę liczbę dostępnych opcji ORM, może istnieć taka, która jest lepsza niż ta, z której już korzystasz. Powstaje pytanie: co się stanie, kiedy to zrobisz? Repozytorium daje niezłą abstrakcję. Byłem tam i przede wszystkim chciałbym zrobić taką abstrakcję.
devnull
3
@devnull Nie zgadzam się. Gdyby zdarzyło się to co najmniej raz, uważałbym to za akceptowalne ryzyko. Jeśli boisz się dokonać złego wyboru: eksperymentuj więcej, zanim zdecydujesz się na jeden. Teoretycznie taka abstrakcja brzmi nieźle, ale w praktyce kończysz odtwarzanie dość dużej części API swojej ORM, wszystko dlatego, że gdzieś kiedyś możesz skończyć wybierając inną. Dla mnie to marnowany wysiłek, zbędny kod i więcej kodów, które sam musisz zachować i zagwarantować. Poza tym zmiana ORM nie powinna wpływać na całą aplikację; naucz się ustawiać granice domen.
Stefan Billiet
Zmiana ORM nie jest jedynym powodem repozytorium. Jeśli musisz wprowadzić [rozproszoną] pamięć podręczną w swojej aplikacji - wszystkie zmiany zostaną wprowadzone w repozytorium, a Twój BL nie zmieni się z powodu zmian warstwy dostępu do danych.
Valery,
czy rozproszona pamięć podręczna nie byłaby wbudowana w ORM w większości przypadków?
user1450877,
Myślę, że to zależy od ORM. Możesz zdecydować się na lżejsze ORM niż NHibernate lub EF.
Valery,
2

1) Co robisz, jeśli chcesz wyłączyć ORM, miałbyś w aplikacji określony kod ORM, gdybyś nie zawierał go w repozytorium.

Nie byłem jeszcze w sytuacji, gdy firma nagle postanowiła zmienić technologię dostępu do danych. Jeśli tak się stanie, konieczne będą pewne prace. Mam tendencję do abstrakcyjnego dostępu do danych poprzez interfejsy. Repozytorium jest jednym ze sposobów rozwiązania tego problemu.

Miałbym wtedy inny zestaw do konkretnej implementacji mojej warstwy dostępu do danych. Na przykład mogę mieć:

Company.Product.Datai Company.Product.Data.EntityFrameworkzespoły. Pierwszy zestaw byłby używany wyłącznie do interfejsów, a inny byłby konkretnym wdrożeniem logiki dostępu do danych Entity Framework.

2) Czy wzorzec repozytorium jest nadal aktualny, gdy nie używasz ORM i sam używasz ADO.net do dostępu do danych i zapełniania danych obiektowych?

Myślę, że to ty decydujesz, który wzór jest ważny, czy nie. Użyłem wzorca repozytorium w warstwie prezentacji. Należy pamiętać, że ludzie lubią wrzucać obowiązki do repozytoriów. Zanim się zorientujesz, twoja klasa repozytorium będzie tańczyć, śpiewać i robić różne rzeczy. Chcesz tego uniknąć.

Widziałem klasę repozytorium, która zaczęła się od odpowiedzialności za GetAll, GetById, Update i Delete, co jest w porządku. Do czasu zakończenia projektu ta sama klasa dysponowała dziesiątkami metod (obowiązków), których nigdy nie powinno być. Na przykład GetByForename, GetBySename, UpdateWithExclusion i wszelkiego rodzaju szalone rzeczy.

Tutaj pojawiają się zapytania i polecenia.

3) Jeśli używasz ORM, ale nie wzorca repozytorium, gdzie przechowujesz najczęściej używane zapytania. Czy mądrze byłoby reprezentować każde zapytanie jako klasę i mieć jakąś fabrykę zapytań do tworzenia instancji?

Myślę, że bardzo dobrym pomysłem jest stosowanie zapytań i poleceń zamiast repozytoriów. Wykonuję następujące czynności:

  • Zdefiniuj interfejs dla zapytania. Pomoże to w teście jednostkowym. Na przykładpublic interface IGetProductsByCategoryQuery { ... }

  • Zdefiniuj konkretną implementację zapytania. Będziesz mógł wstrzykiwać je za pośrednictwem wybranych ram IoC. Na przykładpublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Teraz zamiast zanieczyszczać repozytorium dziesiątkami obowiązków, po prostu grupuję moje zapytania w przestrzeni nazw. Na przykład interfejs dla powyższego zapytania może istnieć w: Company.SolutionName.Products.Queriesa implementacja może istniećCompany.SolutionName.Products.Queries.Implementation

Jeśli chodzi o aktualizację lub usuwanie danych, używam wzorca poleceń w ten sam sposób.

Niektórzy mogą się nie zgodzić i powiedzieć, że zanim projekt zostanie ukończony, będziesz mieć dziesiątki klas i przestrzeni nazw. Tak, będziesz. Moim zdaniem to dobrze, że możesz przeglądać wybrane przez siebie rozwiązanie w IDE i od razu zobaczyć, jakie obowiązki ma dany komponent. Jeśli zamiast tego zdecydowałeś się zastosować wzorzec repozytorium, będziesz musiał zajrzeć do każdej klasy repozytorium, próbując wywiązać się ze swoich obowiązków.

CodeART
źródło
Podoba mi się pomysł posiadania poleceń zamiast ogólnych funkcji. Gdzie mogę przeczytać więcej o tym, jak je wdrożyć w kontekście dostępu do danych?
ankush981,
1

ZASTRZEŻENIE: To, co następuje, oparte jest na moim zrozumieniu i krótkim doświadczeniu z tego wzorca (to znaczy Repozytorium). Może robię to źle ... w rzeczywistości jestem całkiem pewny, że robię to źle :). Tak więc, choć jest to próba odpowiedzi, jest to również pytanie ukryte.

Używam wzorca repozytorium, aby wyodrębnić warstwę dostępu do danych, która w większości przypadków jest ORM. Jest to ogólny interfejs z metodami tworzenia, odczytu, aktualizacji, usuwania i implementacji klas LINQ na SQL i EF. Mógłbym wykonać implementację, która zapisuje do plików XML na dysku (po prostu dziki przykład tego, co mogłem z tym zrobić). Nie wdrażam jednostki pracy, ponieważ jest ona obsługiwana przez ORM. W razie potrzeby prawdopodobnie mógłbym to wdrożyć. Podoba mi się to w ten sposób, ponieważ do tej pory zapewniło mi przyjemną separację. Nie twierdzę, że nie ma lepszych alternatyw.

Aby odpowiedzieć na pytania:

  1. Czy ci się to podoba, czy nie, zmiana nastąpi. A jeśli napisałeś wiele aplikacji i nadszedł czas, aby je utrzymać, ale czujesz, że praca z obecnym ORM jest trudna i chcesz go zmienić, pochwalisz się za zrobienie takiej abstrakcji. Po prostu użyj czegokolwiek, z czym czujesz się komfortowo, czy to Repozytorium, czy inny wzór / koncepcja.
  2. Tak, dopóki używasz go do oddzielenia dostępu do danych. Jak wspomniałem wcześniej, możesz wykonać implementację zapisującą dane w plikach płaskich.
  3. Używam Repozytorium do przechowywania moich często używanych zapytań i Obiektów Zapytań, gdy mam zapytania, które chcę poprawić (których nie jest tak wiele).

Aby dać wam inny przykład, chłopaki z Umbraco wyodrębnili pojemnik DI, na wypadek gdyby chcieli przejść na coś innego.

diabelnie
źródło
0

Jeśli ORM oferuje elastyczność LINQ, chętny ładowanie itp., Nie ukryłbym tego za żadną dodatkową warstwą.
Jeśli dotyczy to surowego sql (mikro ORM), to i tak należy użyć „metody na zapytanie”, aby uzyskać możliwość ponownego użycia, dzięki czemu wzór repozytorium jest dobrze dopasowany.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

Dlaczego musiałbyś się zmienić?
Należy użyć tej, która ma większość potrzebnych funkcji. Możliwe, że nowa wersja ormX wprowadza nowe funkcje i okazuje się lepsza niż obecna, ale ...
Jeśli zdecydujesz się ukryć orm, możesz korzystać tylko z funkcji wspólnych dla wszystkich kandydatów.
Np. Nie możesz używać Dictionary<string, Entity>właściwości w swoich bytach, ponieważ ormY nie może sobie z nimi poradzić.

Zakładając LINQ jest używany, większość z przełącznikiem ORM jest tylko przełączanie referencje biblioteki i zastąpienie session.Query<Foo>()z context.Fooslub podobny aż kompiluje. Nudne zadanie, ale zajmuje mniej czasu niż kodowanie warstwy abstrakcji.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Tak. Kod powinien być wielokrotnego użytku, co oznacza umieszczenie budynku SQL, materializacji obiektu itp. W jednym miejscu (osobna klasa). Równie dobrze możesz nazwać klasę „XRepository” i wyodrębnić z niej interfejs.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

Zakładając, że użyto LINQ, opakowanie klasy byłoby przesadą IMHO. Lepszym sposobem byłyby metody rozszerzenia

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

Każdy kod, który jest używany w wielu miejscach (lub ma potencjał) i jest wystarczająco skomplikowany (podatny na błędy), powinien zostać wyodrębniony w scentralizowane miejsce.

Mike Koder
źródło