Zastanawiam się, jak należy grupować repozytoria? Podobnie jak w przykładach, które widziałem w mvc asp.net, aw moich książkach zasadniczo używają jednego repozytorium na tabelę bazy danych. Ale wydaje się, że wiele repozytoriów prowadzi do tego, że później będziesz musiał wywoływać wiele repozytoriów w celu kpiny i innych rzeczy.
Więc myślę, że powinienem je pogrupować. Jednak nie jestem pewien, jak je pogrupować.
W tej chwili utworzyłem repozytorium rejestracji, aby obsłużyć wszystkie moje sprawy rejestracyjne. Jednak są jakieś 4 tabele, które muszę zaktualizować, a zanim miałem 3 repozytoria, aby to zrobić.
Na przykład jedna z tabel jest tabelą licencji. Kiedy się rejestrują, patrzę na ich klucz i sprawdzam, czy istnieje w bazie danych. Co się teraz stanie, jeśli będę musiał sprawdzić ten klucz licencyjny lub coś innego z tej tabeli w innym miejscu niż rejestracja?
Jedno miejsce może być zalogowane (sprawdź, czy klucz nie wygasł).
Więc co bym zrobił w tej sytuacji? Przepisz kod ponownie (przerwij DRY)? Spróbuj po prostu połączyć te 2 repozytoria razem i miej nadzieję, że żadna z metod nie będzie potrzebna w innym momencie (na przykład może mam metodę, która sprawdza, czy używana jest nazwa_użytkownika - może będę potrzebować tego gdzie indziej).
Również jeśli połączę je ze sobą, potrzebowałbym albo 2 warstw usług, które przechodzą do tego samego repozytorium, ponieważ myślę, że posiadanie całej logiki dla 2 różnych części witryny byłoby długie i musiałbym mieć nazwy takie jak ValidateLogin (), ValdiateRegistrationForm () , ValdiateLoginRetrievePassword () itd.
Albo zadzwoń do Repozytorium i po prostu masz dziwnie brzmiącą nazwę?
Po prostu wydaje się trudne stworzenie repozytorium, które ma wystarczająco ogólną nazwę, aby można było go używać w wielu miejscach aplikacji i nadal ma sens, a nie sądzę, aby wywoływanie innego repozytorium w repozytorium było dobrą praktyką?
źródło
Odpowiedzi:
Jedna rzecz, którą zrobiłem źle, bawiłem się wzorcem repozytorium - tak jak ty, myślałem, że ta tabela odnosi się do repozytorium 1: 1. Kiedy zastosujemy pewne reguły z Domain Driven Design - problem grupowania repozytoriów często znika.
Repozytorium powinno znajdować się w zbiorczym katalogu głównym, a nie w tabeli. Oznacza to - jeśli podmiot nie powinien żyć sam (tj. - jeśli masz element,
Registrant
który w szczególności uczestniczyRegistration
) - to tylko podmiot, nie potrzebuje repozytorium, powinien zostać zaktualizowany / utworzony / odzyskany przez repozytorium zagregowanego roota należy.Oczywiście - w wielu przypadkach ta technika zmniejszania liczby repozytoriów (właściwie - jest to bardziej technika strukturyzowania modelu domeny) nie może być zastosowana, ponieważ każda jednostka ma być zbiorczym korzeniem (to w dużym stopniu zależy od Twojej domeny, Mogę podać tylko ślepe domysły). W twoim przykładzie -
License
wydaje się być zbiorczym korzeniem, ponieważ musisz mieć możliwość sprawdzenia ich bez kontekstuRegistration
encji.Ale to nie ogranicza nas do repozytoriów kaskadowych (
Registration
repozytorium może odwoływać się doLicense
repozytorium w razie potrzeby). To nie ogranicza nas do odwoływania się doLicense
repozytorium (najlepiej - przez IoC) bezpośrednio zRegistration
obiektu.Po prostu staraj się nie popychać swojego projektu przez komplikacje wynikające z technologii lub niezrozumienie czegoś. Grupowanie repozytoriów
ServiceX
tylko dlatego, że nie chcesz tworzyć dwóch repozytoriów, nie jest dobrym pomysłem.Znacznie lepiej byłoby nadać mu odpowiednią nazwę -
RegistrationService
tjGeneralnie jednak należy unikać usług - często są one przyczyną, która prowadzi do anemicznego modelu domeny .
EDYCJA:
Zacznij używać IoC. Naprawdę łagodzi ból wstrzykiwania uzależnień.
Zamiast pisać:
var registrationService = new RegistrationService(new RegistrationRepository(), new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());
będziesz potrafił napisać:
var registrationService = IoC.Resolve<IRegistrationService>();
Ps Lepiej byłoby użyć tak zwanego wspólnego lokalizatora usług, ale to tylko przykład.
źródło
Jedną z rzeczy, które zacząłem robić, aby rozwiązać ten problem, jest faktyczne rozwijanie usług obejmujących repozytoria N. Miejmy nadzieję, że frameworki DI lub IoC mogą to ułatwić.
public class ServiceImpl { public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { } }
Czy to ma sens? Rozumiem też, że mówienie o usługach w tym dworku może, ale nie musi, być zgodne z zasadami DDD, po prostu robię to, ponieważ wydaje się działać.
źródło
To, co robię, to abstrakcyjna klasa bazowa zdefiniowana w następujący sposób:
public abstract class ReadOnlyRepository<T,V> { V Find(T lookupKey); } public abstract class InsertRepository<T> { void Add(T entityToSave); } public abstract class UpdateRepository<T,V> { V Update(T entityToUpdate); } public abstract class DeleteRepository<T> { void Delete(T entityToDelete); }
Następnie możesz wyprowadzić repozytorium z abstrakcyjnej klasy bazowej i rozszerzyć swoje pojedyncze repozytorium, o ile na przykład argumenty generyczne różnią się;
public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>, ReadOnlyRepository<string, IRegistrationItem>
itp....
Potrzebuję oddzielnych repozytoriów, ponieważ mamy ograniczenia dotyczące niektórych naszych repozytoriów, co zapewnia nam maksymalną elastyczność. Mam nadzieję że to pomoże.
źródło
Mam to jako moją klasę repozytorium i tak, rozszerzam w repozytorium tabeli / obszaru, ale nadal czasami muszę zepsuć DRY.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MvcRepository { public class Repository<T> : IRepository<T> where T : class { protected System.Data.Linq.DataContext _dataContextFactory; public IQueryable<T> All() { return GetTable.AsQueryable(); } public IQueryable<T> FindAll(Func<T, bool> exp) { return GetTable.Where<T>(exp).AsQueryable(); } public T Single(Func<T, bool> exp) { return GetTable.Single(exp); } public virtual void MarkForDeletion(T entity) { _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity); } public virtual T CreateInstance() { T entity = Activator.CreateInstance<T>(); GetTable.InsertOnSubmit(entity); return entity; } public void SaveAll() { _dataContextFactory.SubmitChanges(); } public Repository(System.Data.Linq.DataContext dataContextFactory) { _dataContextFactory = dataContextFactory; } public System.Data.Linq.Table<T> GetTable { get { return _dataContextFactory.GetTable<T>(); } } } }
EDYTOWAĆ
public class AdminRepository<T> : Repository<T> where T: class { static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString); public AdminRepository() : base( dc ) { }
Mam też kontekst danych, który został utworzony przy użyciu klasy Linq2SQL.dbml.
Więc teraz mam standardowe repozytorium implementujące standardowe wywołania, takie jak All i Find, aw moim AdminRepository mam określone wywołania.
Nie odpowiada na pytanie DRY, chociaż nie sądzę.
źródło
Oto przykład ogólnej implementacji repozytorium przy użyciu FluentNHibernate. Jest w stanie utrwalić każdą klasę, dla której napisałeś mapper. Jest nawet w stanie wygenerować bazę danych na podstawie klas mappera.
źródło
Wzorzec repozytorium to zły wzorzec projektowy. Pracuję z wieloma starymi projektami .Net i ten wzorzec zwykle powoduje błędy „Transakcje rozproszone”, „Częściowe wycofanie zmian” i „Wyczerpana pula połączeń”, których można było uniknąć. Problem polega na tym, że wzorzec próbuje wewnętrznie obsługiwać połączenia i transakcje, ale te powinny być obsługiwane w warstwie kontrolera. Również EntityFramework już abstrakcjonuje wiele logiki. Sugerowałbym zamiast tego użycie wzorca usługi, aby ponownie użyć udostępnionego kodu.
źródło
Proponuję przyjrzeć się Sharp Architecture . Sugerują użycie jednego repozytorium na jednostkę. Obecnie używam go w swoim projekcie i jestem bardzo zadowolony z efektów.
źródło