Do repozytorium lub nie do repozytorium


10

Kiedy po raz pierwszy dowiedziałem się o Domain Driven Design, zapoznałem się również z repozytorium i wzorcami jednostek pracy, które kiedyś wydawały się być na najwyższym poziomie dla fajnych dzieciaków, które rzucały zapytania SQL, takie jak jaskiniowcy, na bazy danych. Im bardziej zagłębiłem się w ten temat, tym bardziej dowiedziałem się, że nie wydają się one już konieczne z powodu ORM, takich jak EF i NHibernate, które implementują zarówno jednostkę pracy, jak i repozytoria w jednym interfejsie API, zwanym sesją lub kontekstem.

Teraz nie jestem pewien, co robić. Do repozytorium lub nie do repozytorium. Naprawdę rozumiem argument, że takie nieszczelne abstrakcje tylko nadmiernie komplikują rzeczy, dodając absolutnie nic, co mogłoby uprościć dostęp do danych, jednak nie wydaje się słuszne łączenie każdego możliwego aspektu mojej aplikacji np. Z Entity Framework . Zazwyczaj przestrzegam kilku prostych wskazówek:

  1. Warstwa domenowa jest sercem systemu, zawierającym podmioty, usługi, repozytoria ...
  2. Warstwa infrastruktury zapewnia implementacje interfejsów domenowych związanych z infrastrukturą, np. Plik, baza danych, protokoły.
  3. Warstwa aplikacji zawiera katalog główny kompozycji, który porządkuje i porządkuje wszystko.

Moje rozwiązania zwykle wyglądają tak:

Domain.Module1
Domain.Module2
    IModule2Repo
    IModule2Service
    Module2
Infrastructure.Persistence
    Repositories
        EntityFrameworkRepositoryBase
MyApp
    Boostrapper
        -> inject EntityFrameworkRepositoryBase into IRepository etc.

Utrzymuję moją warstwę domeny w czystości, używając IRepository<'T>również domeny, która nie zależy od niczego innego, co mówi mi, jak uzyskać dostęp do danych. Gdybym teraz dokonał konkretnej implementacji, IModule2Servicektóra wymaga dostępu do danych, musiałbym wstrzyknąć, DbContexta przez to połączyć ją bezpośrednio z warstwą infrastruktury. ( Wchodząc do projektu Visual Studio, może to okazać się bardzo trudne z powodu zależności cyklicznych! )

Dodatkowo Co może być alternatywą dla depozytów i fukktonów dzieł ? CQRS? Jak można wyodrębnić ramy czysto infrastrukturalne?


1
Nawiasem mówiąc, jeśli mówimy o „repozytorium”, jak opisano w DDD, wówczas EF i nHibernate nie są repozytoriami. Jasne, częściowo abstrakują mechanizm dostępu do danych, ale repozytorium ma znacznie więcej .
Eric King,

Odpowiedzi:


4

W inżynierii chodzi o kompromisy. Podobnie jest z tworzeniem oprogramowania. Obecnie uważam, że tylko inną opcją, która byłaby prostsza, jest bezpośrednia praca z ORM. Ale tak jak powiedziałeś, może to zablokować cię w określonych ramach trwałości.

Musisz więc zadać sobie pytanie: „Czy dodatkowa złożoność jest warta oddzielenia kodu od trwałości”? Za każdym razem, gdy słyszę, jak ludzie mówią, że chcą oddzielić swój kod od wytrwałości, pytam: „Ile razy w swojej karierze zmieniałeś swój schemat wytrwałości?”

Problem z repozytoriami polega na tym, że utrudniają one wspólne zmiany. Na przykład. dodając nowy sposób zapytania o agregację. I sprawiają, że nietypowe zmiany (podobno) są łatwiejsze. Na przykład. zmiana implementacji repozytoriów.

Jest też argument testowania jednostek, na który mówię: „Jeśli środowisko utrwalania nie pozwala na wyśmiewanie bazy danych, ani w pamięci, ani lokalnie, to w ogóle nie warto używać tego środowiska”.


1
Celem repozytorium jest oddzielenie aplikacji od trwałości i nie wymaga ona złożoności. I zmieniam szczegóły dotyczące trwałości przynajmniej raz, ponieważ dostęp do bazy danych jest ostatnią rzeczą, którą koduję, do tego momentu używam w repozytoriach pamięci. Poza tym istnieje bardzo subtelna pułapka, gdy bezpośrednio używasz szczegółów trwałości. Twoje obiekty biznesowe będą zwykle zaprojektowane tak, aby były z nim zgodne, zamiast agnostyk. A to dlatego, że bogatej, poprawnie zamkniętej encji nie można bezpośrednio przywrócić z żadnej pamięci (z wyjątkiem pamięci) bez (pewnych brzydkich) obejść
MikeSW

O dodaniu nowego sposobu zapytania o zagregowany katalog główny lub dowolny byt, dlatego pojawił się CQRS. Osobiście przechowuję repozytorium na potrzeby domeny i używam programów obsługi zapytań do faktycznego zapytania. I te programy obsługi są bardzo ściśle powiązane z db i ofc bardzo wydajne w tym, co robią.
MikeSW,

3

Jestem repozytorium, chociaż odszedłem od ogólnych wzorców repozytorium. Zamiast tego dopasowuję moje repozytoria do funkcji biznesowej, którą pełnią. Repozytoria nie mają na celu wyodrębnienia ORM, ponieważ nie jest to coś, czego spodziewałbym się zmienić, a jednocześnie unikam nadmiernego granulacji repozytorium. (Tj. CRUD) Zamiast tego moje repozytoria służą od dwóch do trzech kluczowych celów:

  • Odzyskiwanie danych
  • Tworzenie danych
  • Twarde usunięcie

Do pobierania danych repozytorium zawsze zwraca IQueryable<TEntity>. Do tworzenia danych zwraca TEntity. Repozytorium obsługuje moje filtrowanie na poziomie podstawowym, takie jak stan „aktywny” autoryzacji dla systemów używających wzorców miękkiego usuwania oraz stan „bieżący” dla systemów korzystających z danych historycznych. Tworzenie danych odpowiada tylko za upewnienie się, że wymagane odniesienia zostały rozwiązane i powiązane, a jednostka jest skonfigurowana i gotowa do pracy.

Za aktualizację danych odpowiada logika biznesowa współpracująca z danymi podmiotami. Może to obejmować np. Reguły sprawdzania poprawności. Nie próbuję kapsułkować tego w metodzie repozytorium.

Usunięcie w większości moich systemów jest programowo usuwane, więc podlegałoby aktualizacji danych. (IsActive = false) W przypadku twardego usuwania byłoby to jedno-liniowe w repozytorium.

Dlaczego repozytoria? Zdolność testowa. Jasne, DbContext można wyśmiewać, ale łatwiej jest wyśmiewać klasę, która zwracaIQueryable<TEntity>. Świetnie się też bawią wzorcem UoW, osobiście używam wzorca DbContextScope Mehdime, aby rozszerzyć jednostkę pracy na pożądanym poziomie (tj. Kontrolery w MVC) i pozwolić moim kontrolerom i klasom usług pomocniczych korzystać z repozytoriów bez konieczności przekazywania referencji do UoW / dbContext wokół. Używanie IQueryable oznacza, że ​​nie potrzebujesz wielu metod otoki w repozytorium, a twój kod może zoptymalizować sposób, w jaki dane zostaną wykorzystane. Na przykład repozytorium nie musi ujawniać metod takich jak „Exists” lub „Count” ani próbować owijać encji innymi obiektami POCO w przypadkach, w których potrzebne są podzestawy danych. Nie muszą nawet obsługiwać opcji szybkiego ładowania powiązanych danych, które mogą być lub nie być potrzebne. Przekazując IQueryable, kod wywołujący może:

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

Bardzo elastyczny, a z testowego POV moje wyśmiewane repozytorium po prostu musi zwrócić Listy wypełnionych obiektów obiektów.

Jeśli chodzi o ogólne repozytoria, w większości się od nich wycofałem, ponieważ kiedy kończysz się repozytorium na tabelę, twoje kontrolery / usługi kończą się odniesieniami do kilku repozytoriów, aby wykonać jedną akcję biznesową. W większości przypadków tylko jedno lub dwa repozytoria faktycznie wykonują operacje zapisu (pod warunkiem, że poprawnie używasz właściwości nawigacji), podczas gdy reszta obsługuje te z odczytami. Wolę mieć coś takiego jak OrderRepository, które jest w stanie czytać i tworzyć zamówienia oraz czytać odpowiednie wyszukiwania itp. (Lekkie obiekty klientów, produkty itp.) W celach informacyjnych podczas tworzenia zamówienia, niż uderzać w 5 lub 6 różnych repozytoriów. Może to naruszać purystów DNRY, ale moim argumentem jest to, że celem repozytorium jest tworzenie zamówień, które zawierają powiązane odniesienia.Repository<Product>aby uzyskać produkty, w przypadku których na podstawie zamówienia potrzebuję tylko podmiotu z garstką pól. Moje OrderRepository może mieć zwracaną .GetProducts()metodę, IQueryable<ProductSummary>która wydaje mi się lepsza niż ta, Repository<Product>która w końcu ma kilka metod „Get”, aby spróbować obsługiwać różne obszary potrzeb aplikacji i / lub jakieś złożone wyrażenie filtrujące.

Wybieram prostszy kod, który jest łatwy do naśladowania, testowania i strojenia. Może być nadużywane w zły sposób, ale wolę mieć coś, w którym nadużycia są łatwe do wykrycia i poprawienia, niż próbować „zablokować” kod w sposób, który nie może być nadużywany, zawieść, a następnie mieć coś, co jest koszmar, aby rzeczywiście zrobić to, co klient płaci, aby to zrobić w końcu. :)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.