Najlepszy sposób na implementację wzorca repozytorium?

Badałem BDD/DDD i w konsekwencji starałem się wymyślić właściwą implementację wzorca repozytorium. Do tej pory trudno było znaleźć konsensus co do najlepszego sposobu wdrożenia tego. Próbowałem sprowadzić to do następujących odmian, ale nie jestem pewien, które jest najlepsze podejście.

W celach informacyjnych buduję ASP.Aplikacja MVC z NHibernate jako back-end.

public interface IRepository<T> {
        // 1) Thin facade over LINQ
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IQueryable<T> Find();
        // or possibly even
        T Get(Expression<Func<T, bool>> query);
        List<T> Find(Expression<Func<T, bool>> query);
}

public interface IRepository<T> {
        // 2) Custom methods for each query
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySku(string sku);
        IList<T> FindByName(string name);
        IList<T> FindByPrice(decimal price);
        // ... and so on
}

public interface IRepository<T> {
        // 3) Wrap NHibernate Criteria in Spec pattern
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}


public interface IRepository<T> {
        // 4) Expose NHibernate Criteria directly
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> Find(ICriteria criteria);
        // .. or possibly
        IList<T> Find(HQL stuff);
}

Moje początkowe myśli są takie, że

1) jest wielki z punktu wydajności widzenia, ale mogę mieć kłopoty, gdy sprawy się skomplikują.

2) wydaje się bardzo żmudny i może skończyć się bardzo zatłoczoną klasą, ale poza tym oferuje wysoki stopień oddzielenia między logiką domeny a warstwą danych, którą lubię.

3) wydaje się trudne z góry i więcej pracy do pisania zapytań, ale ogranicza zanieczyszczenie krzyżowe tylko do warstwy specyfikacji.

4) moja najmniej ulubiona, ale prawdopodobnie najbardziej bezpośrednia implementacja i prawdopodobnie najbardziej wydajna baza danych dla złożonych zapytań, choć wiąże się to z dużą odpowiedzialnością za Kod wywoławczy.

Author: Jim G., 2009-09-11

7 answers

Myślę, że wszystkie są dobre opcje (może z wyjątkiem 4, Jeśli nie chcesz związać się z nhibernate), i wydaje się, że masz plusy i minusy dobrze przeanalizowane, aby podjąć decyzję na własną rękę w oparciu o swoje obecne wysiłki. Nie bij siebie zbyt mocno.

Obecnie pracuję nad mieszanką między 2 a 3 chyba:

public interface IRepository<T> 
{
        ...
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}

public interface ISpecificRepository : IRepository<Specific> 
{
        ...
        IList<Specific> FindBySku(string sku);
        IList<Specific> FindByName(string name);
        IList<Specific> FindByPrice(decimal price);
}

I jest też repozytorium (T) klasy bazowej.

 10
Author: Fredy Treboux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 02:43:39

Jest też dobry argument za podejściem" żadne z powyższych".

Problem z repozytoriami generycznymi polega na tym, że zakładasz, że wszystkie obiekty w Twoim systemie będą obsługiwały wszystkie cztery operacje CRUD: Tworzenie, odczyt, aktualizacja, usuwanie. Ale w złożonych systemach prawdopodobnie będziesz mieć obiekty, które obsługują tylko kilka operacji. Na przykład możesz mieć obiekty tylko do odczytu lub obiekty utworzone, ale nigdy nie aktualizowane.

You could break the IRepository interfejs do małych interfejsów, do odczytu, usuwania, itp. ale to robi się bałagan dość szybko.

Gregory Young podaje dobry argument (z perspektywy warstw DDD / software), że każde repozytorium powinno obsługiwać tylko operacje, które są specyficzne dla obiektu domeny lub agregatu, z którym pracujesz. Oto jego artykuł o repozytoriach generycznych .

[[0]}i alternatywny widok, zobacz ten Ayende blog post .
 22
Author: dthrasher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 00:14:39

Jedną z rzeczy, które robimy, jest to, że wszystkie nasze repozytoria mają różne potrzeby, więc tworzymy kolekcję interfejsów:

public interface IReadOnlyRepository<T,V>
{
   V Find(T);
}

W tym przykładzie repozytorium tylko do odczytu po prostu pobiera z bazy danych. Powodem T, V jest to, że V reprezentuje to, co jest zwracane przez repozytorium, A T reprezentuje to, co jest przekazywane, więc możesz zrobić coś takiego:

public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer>
{
    public Customer Find(int customerId)
    {
    }

    public Customer Find(string customerName)
    {
    }
}

Mogę również tworzyć oddzielne interfejsy dla Add, Update i Delete. This way if my repozytorium nie potrzebuje takiego zachowania, wtedy po prostu nie implementuje interfejsu.

 6
Author: Michael Mann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 03:58:33

Jestem fanem bif 1, ponieważ mogę tworzyć filtry i metody rozszerzeń stronicowania, niż mogę zastosować do IQueryable wartości zwracanych dla metody Find. Metody rozszerzeń przechowuję w warstwie danych, a następnie konstruuję w locie w warstwie biznesowej. (Nie do końca czysty, co prawda.)

Oczywiście, gdy system się ustabilizuje mam możliwość wykonania konkretnych metod wyszukiwania przy użyciu tych samych metod rozszerzeń i optymalizacji przy użyciu Func.

 1
Author: Scott McKenzie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 03:11:46

Podczas używania NH z Linq Twoje repozytorium może być:

session.Linq<Entity>()

Specyfikacje są rzeczy, które dotyczą:

IQueryable<Entity>

Możesz to wszystko wymodelować, jeśli chcesz, ale jest to dużo przyziemnej pracy do abstrakcji.

Proste jest dobre. Yah, NH robi bazy danych, ale dostarcza o wiele więcej wzorców. Posiadanie innych niż DAL zależy od NH jest dalekie od grzechu.
 1
Author: anonymous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 14:55:10

Wierzę, że to zależy od twoich potrzeb. Ważne jest, aby myśleć o repozytorium w połączeniu z innymi wzorcami projektowymi, które uważasz za używane. Wreszcie, to bardzo zależy od tego, czego oczekujesz od repozytorium (jakie są główne powody jego używania).

Czy musisz utworzyć ścisłą warstwę (na przykład będziesz musiał zastąpić NHibernate frameworkiem encji w przyszłości)? Czy chcesz napisać test specjalnie do metod repozytorium?

Nie ma najlepszego sposobu na tworzenie repozytorium. Jest tylko kilka sposobów i to zdecydowanie zależy od ciebie, co jest najbardziej praktyczne dla Twoich potrzeb.

 1
Author: Miroslav Holec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-07 18:17:43

Wither The repozytorium

Odpowiedni artykuł Jimmy Bogard z LosTechies

Http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/10/wither-the-repository.aspx

Kolejny szybki artykuł z kilkoma komentarzami sugerującymi, że Wersja #2 jest tak naprawdę wzorcem DOA, a nie repozytorium.

Http://fabiomaulo.blogspot.com/2009/06/linq-and-repository.html

 0
Author: Michael Cook,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 16:21:28